Javascript基础知识点整理六:DOM和事件

IFE     2019年03月30日     分类:   前端知识体系     标签:   javascript     浏览量:   2025


1,DOM的介绍

浏览器中的JavaScript遵循两个标准:ECMA 262标准(js基础知识,语法核心), 和W3C标准(没有规定任何JS基础相关的东西,不管什么变量类型、原型、作用域和异步,只管定义 用于浏览器中JS操作页面的API和全局变量)

DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容),它遵循W3C标准。

DOM可以理解为:浏览器把拿到的html代码,结构化一个浏览器能识别并且js可操作的一个模型而已。它的数据结构是:树。

DOM操作常用API:

<div id="div1" class="class-div1">
    <p id="p1" data-name="p1-data-name">this is p1</p>
    <p id="p2" >this is p2</p>
</div>
<div id="div2">
    <p id="p3">this is p3</p>
    <p id="p4">this is p4</p>
</div>
//获取DOM节点
let div1 = document.getElementById('div1'); //元素
let divList = document.getElementsByTagName('div');//集合
let containerList = document.getElementsByClassName('.container'); //集合
let pList = document.querySelector('p'); //集合
//DOM节点是个对象 可扩展属性
let p = pList[0];
console.log(p.style.width);//获取属性
p.style.width = '100px';//修改属性
p.className = 'p1';
let p1=document.getElementById('p1')
console.log('data-name:'+p1.getAttribute('data-name'))
//获取nodeName 和 nodeType
console.log(p.nodeName);
console.log(p.nodeType);

//添加新节点
let p1 = document.createElement('p');
p1.innerHTML = 'this is p1';
div1.appendChild(p1) //添加新创建的节点
//移动已有节点
let p2 = document.getElementById('p2');
div1.appendChild(p2);
//获取父元素和子元素
let parent = div1.parentElement;
let child = div1.childNodes // [text, p#p1, text, p#p2, text]
//nodeType 类型 元素节点1 属性节点2 文本节点3
console.log(div1.childNodes[0].nodeType) // 3
console.log(div1.childNodes[0].nodeName) // #text
console.log(div1.childNodes[0].nodeValue) // 空格
div1.removeChild(child[0]); //空格是文本节点,p是元素节点

2, 事件

事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。

DOM 事件被发送用于通知代码相关的事情已经发生了。每个事件都是继承自Event 类的对象。

事件的传播:一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。

  1. 第一阶段:从window对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)。
  2. 第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。
  3. 第三阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。

这种三阶段的传播模型,使得同一个事件会在多个节点上触发。

例子:

<div>
  <p>点击</p>
</div>

上面代码中,<div>节点之中有一个<p>节点。

如果对这两个节点,都设置click事件的监听函数(每个节点的捕获阶段和监听阶段,各设置一个监听函数),共计设置四个监听函数。然后,对<p>点击,click事件会触发四次。

var phases = {
  1: 'capture',
  2: 'target',
  3: 'bubble'
};

var div = document.querySelector('div');
var p = document.querySelector('p');

div.addEventListener('click', callback, true);
p.addEventListener('click', callback, true);
div.addEventListener('click', callback, false);
p.addEventListener('click', callback, false);

function callback(event) {
  var tag = event.currentTarget.tagName;
  var phase = phases[event.eventPhase];
  console.log("Tag: '" + tag + "'. EventPhase: '" + phase + "'");
}

// 点击以后的结果
// Tag: 'DIV'. EventPhase: 'capture'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'DIV'. EventPhase: 'bubble'

事件冒泡:事件开始时由最具体的元素(文档中嵌套层次最深的哪个节点)接收,然后逐级向上传播到较为不具体的节点(文档)

事件捕获:不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。

阻止事件传播:如果希望事件到某个节点为止,不再传播,可以使用事件对象的stopPropagation方法。

// 事件传播到 p 元素后,就不再向下传播了
p.addEventListener('click', function (event) {
  event.stopPropagation();
}, true);

// 事件冒泡到 p 元素后,就不再向上冒泡了
p.addEventListener('click', function (event) {
  event.stopPropagation();
}, false);

上面代码中,stopPropagation方法分别在捕获阶段和冒泡阶段,阻止了事件的传播。
但是,stopPropagation方法只会阻止事件的传播,不会阻止该事件触发<p>节点的其他click事件的监听函数。也就是说,不是彻底取消click事件。

事件绑定

1.DOM0级事件处理程序

var btn = document.getElementById('btn1');
btn.onClick = function(){
    alert('clicked')
}

使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行。也就是说,程序中的this引用当前元素

var btn = document.getElementById('btn1');
btn.onClick = function(){
    alert(this.id)   // "btn1"
}

2.DOM2级事件处理程序

var btn = document.getElementById('btn1');
btn.addEventListener('click', function (event) {
    console.log('clicked');
}, false) // 冒泡阶段调用

3.通用事件绑定

function bindEvent(elem, type, fn) {
    elem.addEventListener(type, fn)
}
var a = document.getElementById('link1');
bindEvent(a, 'click', function (e) {
    e.preventDefault();//阻止默认行为
    alert('clicked');
})

4.事件委托 事件委托利用了事件冒泡,只制定一个事件处理程序,就可以管理某一类型的所有事件。使用事件委托,只需在DOM树中尽量高的层次上添加一个事件处理程序。

//代理-监听div1里的所有a标签
var div1 = document.getElementById('div1');
div1.addEventListener('click', function (e) {
    var target = e.target;
    if (target.nodeName === 'A') {
        alert(target.innerHTML)
    }
})

5.结合事件委托的通用绑定事件函数

function bindEvent(elem, type, selector, fn) {
    if (fn == null) {
        fn = selector;
        selector = null;
    }
    elem.addEventListener(type, function (e) {
        var target;
        if (selector) { //代理
            target = e.target;
            //触发事件的目标是否和选择器匹配
            if (target.matches(selector)) {
                //target当做this,e做参数传进fn
                fn.call(target, e);
            }
        } else {
            //不是代理
            fn(e);
        }
    })
}

//使用代理---------
var div1 = document.getElementById('div1');
bindEvent(div1, 'click', 'a', function (e) {
    console.log(this.innerHTML);
})
//不使用代理----------
var a = document.getElementById('a1');
bindEvent(div1, 'click', function (e) {
    console.log(a.innerHTML);
})

2019/4/10更新,更优的事件委托函数

// 之前的事件委托函数有缺陷,比如用户点击的是li里面的span,就没法触发
// 高级版:点击 span后,递归遍历 span 的祖先元素看其中有没有 ul 里面的 li。
  function delegate(element, eventType, selector, fn) {
     element.addEventListener(eventType, e => {
       let el = e.target
       while (!el.matches(selector)) {
         if (element === el) {
           el = null
           break
         }
         el = el.parentNode
       }
       el && fn.call(el, e, el)
     })
     return element
   }
   delegate('ul', 'click', 'li', fn2)

6.事件节流(函数防抖)
原理:快速操作时不执行事件,操作结束时再执行事件

var textarea = document.getElementById('text');
var timeoutId;
textarea.addEventListener('keyup', function () {
    if (timeoutId) {
        clearTimeout(timeoutId);
    }//停了100毫秒后才触发,减少了计算次数
    timeoutId = setTimeout(function () {
        //触发change事件
    }100)
})


评论总数: 0


登陆后才可以评论