DOM事件模型

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

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

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

DOM事件流包括三个阶段:

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

17 DOM事件委托 - 图1

DOM事件模型分为两类

捕获事件:

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

冒泡事件:

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

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

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

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

示例代码:点击这里

注册事件监听

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

target 与 currentTarget的区别

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

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

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

谁先监听谁先执行

  1. <body>
  2. <div class="son">文字</div>
  3. <script>
  4. //冒泡
  5. let son = document.querySelector(".son");
  6. son.addEventListener("click", () => {console.log(1)});
  7. // 监听
  8. son.addEventListener( "click",() => { console.log(2)},true);
  9. </script>
  10. </body>
  11. // 返回 1 , 2

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

不可冒泡事件

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

UI事件

  • load
  • unload
  • scroll
  • resize

焦点事件

  • blur
  • focu

鼠标事件

  • mouseleave
  • mouseenter

例如 scroll事件

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

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

  1. // 样式
  2. #container {
  3. width: 100%;
  4. height: 400px;
  5. border: 1px solid red;
  6. }
  7. #outer {
  8. width: 80%;
  9. height: 600px;
  10. border: 1px solid blue;
  11. }
  12. #inner {
  13. width: 40%;
  14. height: 900px;
  15. border: 1px solid pink;
  16. }
  1. // 网页本体
  2. <div id="grandFather">
  3. <div id="father">
  4. <div id="son">123</div>
  5. </div>
  6. </div>

如下图所示

17 DOM事件委托 - 图2

加上script设置成了冒泡事件

  1. <script>
  2. father.addEventListener(
  3. "scroll",
  4. function () {
  5. console.log("滚动了");
  6. });
  7. </script>

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

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

  1. <script>
  2. // 去除鼠标滚动
  3. father.addEventListener("wheel", function (e) {
  4. console.log("滚动了");
  5. e.preventDefault();
  6. });
  7. // 去除手机触屏滚动
  8. father.addEventListener("touchstart", function (e) {
  9. console.log("滚动了");
  10. e.preventDefault();
  11. });
  12. </script>

自定义事件

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

  1. <body>
  2. <div id="div1">
  3. <button id="button1">点击触发 frank 事件</button>
  4. </div>
  5. <script>
  6. let button = document.querySelector("#button1");
  7. button.addEventListener("click", () => {
  8. const event = new CustomEvent("frank", {
  9. detail: { name: "frank", age: 18 },
  10. bubbles: true,
  11. });
  12. button.dispatchEvent(event);
  13. });
  14. div1.addEventListener("frank", (e) => {
  15. console.log("frank事件触发了");
  16. console.log(e.detail);
  17. });
  18. </script>
  19. </body>

事件委托

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

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

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

  1. <body>
  2. <div id="div1"></div>
  3. <script>
  4. setTimeout(() => {
  5. const button = document.createElement("button");
  6. button.textContent = "click1";
  7. div1.appendChild(button);
  8. }, 1000);
  9. div1.addEventListener("click", (e) => {
  10. // target就可以表示为当前的事件操作的dom
  11. const t = e.target;
  12. // 获取标签的小写
  13. if (t.tagName.toLowerCase() === "button") {
  14. console.log("button 被点击了");
  15. }
  16. });
  17. </script>
  18. </body>

封装好的事件委托

  1. <body>
  2. <div id="div1"></div>
  3. <script>
  4. setTimeout(() => {
  5. const button = document.createElement("button");
  6. button.textContent = "click1";
  7. div1.appendChild(button);
  8. }, 1000);
  9. on("click", "#div1", "button", () => {
  10. console.log("button被点击了2");
  11. });
  12. function on(eventType, element, selector, fn) {
  13. if (!(element instanceof Element)) {
  14. element = document.querySelector(element);
  15. }
  16. element.addEventListener(eventType, (e) => {
  17. const t = e.target;
  18. // 判断传进来的element是否符合button
  19. while (!t.matches(selector)) {
  20. // 找到最顶层 也就是 #div1 找不到了就停止
  21. if (element === t) {
  22. t = null;
  23. break;
  24. }
  25. // 传进的元素等于他的爸爸
  26. t = t.parentNode;
  27. }
  28. t && fn.call(t, e, t); // 第一个是 this
  29. });
  30. return element;
  31. }
  32. </script>
  33. </body>

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