IFE 2019年03月30日 分类: 前端知识体系 标签: javascript 浏览量: 2025
浏览器中的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是元素节点
事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。
DOM 事件被发送用于通知代码相关的事情已经发生了。每个事件都是继承自Event 类的对象。
事件的传播:一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
这种三阶段的传播模型,使得同一个事件会在多个节点上触发。
例子:
<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) })