DOM事件模型和事件委托

115 阅读3分钟

DOM事件模型

DOM即文档对象模型。是HTML和XML文档的编程接口。

DOM事件:就是用户或浏览器自身执行的某种动作。click,load,onmouseover,onmouseout。

DOM事件流:描述的是从页面中接收事件的顺序

DOM事件流包括三个阶段:

  1. 事件捕获阶段(capture phase
  2. 处于目标阶段(target phase)
  3. 事件冒泡阶段(bubbling phase)

image-20210221111802675

DOM事件模型分为两类

捕获事件:

从外向内找监听函数,DOM树上的表现就是由根节点传播到叶子节点。

冒泡事件:

从内向外找监听函数,DOM树上就是事件从叶子节点传播到根节点。

W3C事件模型:首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段

标准的事件处理模型分为三个阶段:

  1. 父元素中所有的捕捉型事件(如果有)自上而下地执行
  2. 目标元素的冒泡型事件(如果有)
  3. 父元素中所有的冒泡型事件(如果有)自下而上地执行

示例代码:点击这里

注册事件监听

son.addEventListener("event", fn, bool);
// 如果bool不填 就默认是冒泡事件 如果bool为ture就是监听事件

target 与 currentTarget的区别

  1. e.target -用户操作的元素
  2. e.currentTarget -程序员监听的元素
  3. this是e.currentTarget,不推荐使用它

特例:如果监听的是用户点击的div,也就是没有考虑父子同时被监听

div.addEventListener("click", fn);
div.addEventListener("click", fn,ture);

谁先监听谁先执行

	<body>
		<div class="son">文字</div>

		<script>
			//冒泡
			let son = document.querySelector(".son");
			son.addEventListener("click", () => {console.log(1)});
			// 监听
			son.addEventListener(	"click",() => {	console.log(2)},true);
		</script>
	</body>
	// 返回 1 , 2

我们可以取消冒泡但是不能取消监听 e.stopPropagation()这个可以中断冒泡,浏览器就不会再向上走。

不可冒泡事件

引用知乎的一篇文章:JavaScript 中那些不会冒泡的事件

UI事件

  • load
  • unload
  • scroll
  • resize

焦点事件

  • blur
  • focu

鼠标事件

  • mouseleave
  • mouseenter

例如 scroll事件

scroll 事件在元素滚动条在滚动时触发。

我们创建一个网页测试以下

			// 样式
			#container {
				width: 100%;
				height: 400px;
				border: 1px solid red;
			}

			#outer {
				width: 80%;
				height: 600px;
				border: 1px solid blue;
			}
			#inner {
				width: 40%;
				height: 900px;
				border: 1px solid pink;
			}
			// 网页本体
		<div id="grandFather">
			<div id="father">
				<div id="son">123</div>
			</div>
		</div>

如下图所示

image-20210221152230702

加上script设置成了冒泡事件

		<script>
			father.addEventListener(
				"scroll",
				function () {
					console.log("滚动了");
				});
		</script>

如果它是一个冒泡事件当鼠标在#son#father区域滑动时就会在控制台输出滚动了。但是没有输出任何内容。他就是一个不可冒泡事件。同样也对他没法进行e.stopPropagation()阻止冒泡。

我们阻止滚动就用的另一种方法wheeltouchtstart

		<script>
			// 去除鼠标滚动
			father.addEventListener("wheel", function (e) {
				console.log("滚动了");
				e.preventDefault();
			});
			// 去除手机触屏滚动
			father.addEventListener("touchstart", function (e) {
				console.log("滚动了");
				e.preventDefault();
			});
		</script>

自定义事件

创建了一个名称为"frank"可以冒泡但是不能不能阻止冒泡的自定义事件

	<body>
		<div id="div1">
			<button id="button1">点击触发 frank 事件</button>
		</div>

		<script>
			let button = document.querySelector("#button1");
			button.addEventListener("click", () => {
				const event = new CustomEvent("frank", {
					detail: { name: "frank", age: 18 },
					bubbles: true,
				});
				button.dispatchEvent(event);
			});
			div1.addEventListener("frank", (e) => {
				console.log("frank事件触发了");
				console.log(e.detail);
			});
		</script>
	</body>

事件委托

事件委托就是利用事件的冒泡原理,委托他们的父级代为执行事件。

举例:在一个公司里面有同事的快递到了,他有2个方式,一是自己去公司外面拿快递,二是委托前台的同事代为签收。当然第二种方式更加的便捷,同时即使公司来了新员工,前台同事也会在收到寄给新员工的快递后核实并代为签收。

下面就是来了一个新同事span的例子。

	<body>
		<div id="div1"></div>

		<script>
			setTimeout(() => {
				const button = document.createElement("button");
				button.textContent = "click1";
				div1.appendChild(button);
			}, 1000);
			div1.addEventListener("click", (e) => {
                // target就可以表示为当前的事件操作的dom
				const t = e.target;
                // 获取标签的小写
				if (t.tagName.toLowerCase() === "button") {
					console.log("button 被点击了");
				}
			});
		</script>
	</body>

封装好的事件委托

	<body>
		<div id="div1"></div>

		<script>
			setTimeout(() => {
				const button = document.createElement("button");
				button.textContent = "click1";
				div1.appendChild(button);
			}, 1000);
		

				on("click", "#div1", "button", () => {
				console.log("button被点击了2");
			});

			function on(eventType, element, selector, fn) {
				if (!(element instanceof Element)) {
					element = document.querySelector(element);
				}
				element.addEventListener(eventType, (e) => {
					const t = e.target;
					// 判断传进来的element是否符合button
					while (!t.matches(selector)) {
						// 找到最顶层 也就是 #div1  找不到了就停止
						if (element === t) {
							t = null;
							break;
						}
						// 传进的元素等于他的爸爸
						t = t.parentNode;
					}
					t && fn.call(t, e, t); // 第一个是 this
				});
				return element;
			}
		</script>
	</body>

注意:JS不支持事件 ,JS只是调用了DOM提供的addEventListener


资料来源:饥人谷

本文为贰贰的原创文章,著作权归本人和饥人谷所有,转载务必注明来源