/ javascript

javascript事件处理程序

在HTML中内嵌标签属性

<button onclick="alert('hello');">点我啊</button>

缺点:HTML 和 JavaScript 严重耦合,不便与开发维护

DOM属性绑定

element.onclick = function() {
	console.log("hello");
}

优点:兼容性好,上手简单
缺点:一对一绑定,应用场景有限

标准事件处理(代理)函数 (暂不考虑兼容性处理)

添加 addEventListener()
function sayHello() {
	console.log("hello");
}
element.addEventListener("click", sayHello, false);

addEventLitener(type, listener, useCapture)接收三个参数

  • type: 事件类型
  • listener: 事件处理函数
  • useCapture: 默认false代表在冒泡阶段触发listener,如果设置为true则表示用户希望在捕获阶段触发listener
移除 removeEventListener()

addEventListener()一样接收三个参数,常用与临时事件的注销

事件处理的过程

例如注册一个简单的事件处理

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="wrap">
    <button class="btn">点我</button>
  </div>
</body>
</html>

JS:

(function() {
	var sayHi = function(e) {
		console.log("Hi",e.target);
	};
	window.addEventListener("click", sayHi, false);
})()

需要注意的是,事件的触发节点,总是最底层的节点,所以当点击button时浏览器会干两件事情

  • 捕获目标阶段(找到目标节点)
  • 冒泡阶段

Alt text

输出结果:
Alt text

事件代理的好处

在理解事件处的过程后,不难发现父节点(你所注册的事件代理的那个节点,如上就是window)能够接收(在冒泡阶段)所有子孙节点的事件信息,利用这个特性我们就能实现如下优化

  • 减少了过多的事件绑定(一对一的DOM属性绑定)
  • 未知数量的事件处理,或者动态变化的DOM结构的事件处理(全丢给父节点就行了)
实例

1.实现类似codePen标签功能:
Alt text

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div class="wrap">
    <div class="your-in">
      <label for="tags">Tags:</label>
      <input type="text" name="inTags">
    </div>
    <div class="your-tags"></div>
  </div>
</body>
</html>

CSS:

.wrap span{
  display: inline-block;
  margin: 0 6px 0 0;
  padding: 4px 10px;
  padding-left: 0;
  border-radius: 4px;
  background: #eee;
}

.wrap span i{
  margin: 0 4px;
  padding: 2px;
  color: #fff;
  font-style: normal;
  background: #9E9E9E;
  border-radius: 4px;
}

.wrap span i:hover{
  cursor: pointer;
  background: #F44336;
}

.your-tags{
  margin: 20px 0;
  padding: 10px;
  border-top: 1px solid #eee;
}

JS:

(function(){

      /**
       * 数组去重
       */
      Array.prototype.unique = function() {
        var n = {},r=[]; //n为hash表,r为临时数组
        for(var i = 0; i < this.length; i++) {
          if (!n[this[i]]) {
            n[this[i]] = true; //存入 hash 表
        		r.push(this[i]); //把当前数组的当前项 push 到临时数组里面
        	}
        }
        return r;
      }

      /**
       * 用户添加,删除 标签
       */
      var wrap = document.querySelector(".wrap");
      wrap.addEventListener("click", function(e) {
        // 增加标签
        var inTags = document.getElementsByName("inTags");
        inTags[0].onkeyup = function() {
          var your_tags = document.querySelector(".your-tags");
          var a = inTags[0].value.split(",").unique();
          var str = "";
          for(var i=0; i<a.length; i++) {
            if(a[i] != "") {
              str += "<span><i>×</i>"+a[i]+"</span>"
            }
          }
          your_tags.innerHTML = str;
        }
        // 删除标签
        if(e.target.tagName === "I" && e.target.parentNode.tagName === "SPAN") {
          var a = inTags[0].value.split(",").unique();  // 获取用户输入的标签存入数组a
          var ar = [];  // 用于储存删除某一标前后,剩余的标签
          for(var i=0; i<a.length; i++) {
            if(a[i] !== e.target.parentNode.innerText.substr(1)) {
              ar.push(a[i]);
            }
          }
          e.target.parentNode.parentNode.removeChild(e.target.parentNode);
          inTags[0].value = ar.join(",");  // 设置输入框中的值
        }
      }, false);
    })();
    

codePen:

2.实现类似bootstrap模态框功能
Alt text

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div class="wrap">
    <button>点我啊</button>
    <div class="box">
      <div class="msg"><p>hello world</p></div>
    </div>
  </div>
</body>
</html>

CSS:

   .box{
      display: none;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background: rgba(136, 132, 132, 0.6);
    }

    .box .msg{
      margin: 120px auto;
      width: 300px;
      height: 200px;
      border-radius: 4px;
      background: #fff;
    }

    .box .msg p{
      text-align: center;
      font-size: 22px;
      line-height: 100px;
    }

    .show{
      display: block;
    }
      (function(){
        var wrap = document.querySelector(".wrap");
        var box = document.querySelector(".box");
        var msg = document.querySelector(".msg");
        wrap.addEventListener("click", function(e){
          if(e.target.tagName === "BUTTON") {
            box.className = "box show";
          }
        }, false);

        box.addEventListener("click", function() {
          box.className = "box";
        }, false);

        msg.addEventListener("click", function(e){
          e.stopPropagation();  //停止冒泡
        }, false);
      })();

codePen:

以上两个实例(就个人水平而言如果不采用事件代理,要实现相应的功能还是有些困难的)均为我最近遇到的问题,并不一定能完全说明**事件代理**的优点

最后

不暴露你Low的一面,又怎能变优秀,So代码有问题(优化)或者关于js事件处理的称述有不足之处,还望指出

参考资料: