一, DOM
JavaScript 语言核心. 变量的定义, 变量的类型, 运算符, 表达式, 函数, if 语句, for 循环, 算法等等. 这些东西都属于语言核心, 下次继续学习语言核心就是面向对象了. JavaScript 能做非常多的事情: DOM 开发, Ajax 开发, Canvas 开发, Node.JS 开发, 前端框架(React,Vue,Angular 等等),HTML5 开发. 这些都需要语言核心的知识.
DOM 开发说白了就是浏览器中的页面效果开发, 在 2011 年之前, DOM 开发占据了前端开发工程师的 90% 的工作; 但是现在, DOM 开发的工作比重已经降到了 10% 以下. 换句话说, 2011 年之前, 前端 = 做特效的; 2011 年之后, 前端要负责得到后台的数据接口, 用前端 MVC 逻辑分层开发前端组建, 界面, 功能, 还要写 HTML5, 还要做 canvas 动画!
上层的框架屏蔽了下层的语言的一些麻烦, 不方便的东西, 并且提供更方便的 API.
jQuery 就是干这个事情的, 把 JS 中的不方便封装起来, 暴露的 API 都是非常简便的.
jQuery 的哲学就是 DOM 编程领域的霸主, 操作 DOM 节点, 绑定监听, 运动, CSS 样式, Ajax 等等都有封装.
工作上都是用 jQuery, 如果不用 jQuery 也是用类似的东西. 没有人会不用轮子去开发页面效果.
JavaScript 中 Library 表示 "库", 如果这个库的功能很强大, 甚至颠覆了传统编程的语法, 行文习惯, 我们就可以叫做 "框架".
1.1 DOM 是什么
文档对象模型 (DOM,Document Object Model)是 HTML 和 xml 文档的编程接口. 它给文档 (结构树) 提供了一个结构化的表述并且定义了一种方式 - 程序可以对结构树进行访问, 以改变文档的结构, 样式和内容. DOM 提供了一种表述形式 - 将文档作为一个结构化的节点组以及包含属性和方法的对象. 从本质上说, 它将 Web 页面和脚本或编程语言连接起来了.
到底什么是 DOM? 就是你可以像操作对象一样操作 HTML 页面, 而不是操作字符串.
DOM 将 Web 页面和脚本或编程语言连接起来了.
回看一下我们之前学习的 DOM 操作, 都在干嘛? 我们在开发特效, 但是微观的看, 实际上在进行:
1) 得到 HTML 节点
2) 改变节点的属性
3) 改变节点的样式
4) 修改节点, 删除节点, 增加节点
5) 节点之间的关系
1.2 原生 JavaScript 得到节点
document.getElementById('box'); document.getElementsByTagName('p'); |
以上两是全线浏览器都兼容的得到元素方法.
以下这些得到元素的方法都不兼容 IE678.
- document.getElementsByName('aaa')[0] // 通过 name 属性得到元素们
- document.getElementsByClassName('pp') // 通过类名得到元素们
- document.querySelector('#box');
- document.querySelectorAll('#box p'); // 通过选择器得到元素们
jQuery 是 DOM 开发的王者! 帮我们解决了元素选择的兼容问题.
jQuery 底层很强大, 比如 $('.par1')机制不是 getElementsByClassName(), 而是在遍历所有节点, 选择类名有 par1 的项.
二, 原生 JavaScript 节点关系
在 jQuery 中学习 parent(),children(),siblings(),next(),prev()等等节点关系, JS 中也有对应的属性.
原生 JS 提供的节点关系很少:
childNodes,firstChild,lastChild,parentNode,nextSibling,previousSibling
常见的 nodeType 值:
1 - 普通元素节点、比如 div、p 等等 2 - 属性节点 3 - 文本节点 8 - 注释节点 9-document 节点 10 - 文档 DTD |
想要查看某一个元素的节点类型, 直接读取它的 nodeType 属性即可.
改变 nodeType 为 3 的文本节点的内容, 要改变他的 nodeValue 属性
2.1 childNodes 儿子节点
childNodes 在 IE6,7,8 和高级浏览器不一致, 高级浏览器认为所有的换行为空文本节点, 而 IE678 无视这个空文本节点.
div 中没有文本节点, 此时应该是 4 个节点, 但是 IE9,Chrome, 火狐会认为有 9 个节点, IE8 认为有 4 个节点.
高级浏览器会把空文本当做一个节点, 标签前后的空文本也被算作一个.
注释的前后算不算空文本节点, 各个浏览器有不同的解释. 所以用节点的时候, 一定要去过滤, 判断节点的 nodeType 是不是 1.
- <div id="box">
- <p></p>
- <p></p>
- <p></p>
- <p></p>
- </div>
oBox.childNodes.length; //Chrome 数值 9、IE678 是 4 |
为解决兼容性问题(到底空文本算不算儿子, 所以要封装函数来解决):
可以利用 nodeType 是不是 1 来过滤文本节点, 注释节点等, 编写一个函数, 得到一个标签真正的子节点.
jQuery 也有这层过滤:
- // 封装一个 children 函数, 这个函数能返回 obj 对象的所有真正儿子节点
- function chidlren(obj,num){
- var arr = []; // 存储所有儿子
- // 遍历所有的节点
- for(var i = 0; i <obj.childNodes.length;i++){
- // 遍历的过程, 寻找真正的 HTML 儿子节点, 过滤文本, 注释等节点
- // 判断节点类型是不是 1
- if(obj.childNodes[i].nodeType == 1){
- arr.push(obj.childNodes[i]); // 如果是儿子节点就插入数组中
- }
- }
- //return arr;
- // 返回的是: 如果用户传入了 num, 返回某一个儿子, 如果没有 num, 返回所有儿子
- return num ? arr[num] : arr;
- }
- chidlren(oBox)[2].style.backgroundColor = 'red';
- chidlren(oBox,3).style.backgroundColor = 'red';
2.2 parentNode 父亲节点
parentNode 属性表示父亲节点. 任何节点的 parentNode 的 nodeType 一定是 1, 也就是说父亲节点一定是标签节点. 文本节点, 注释节点没有儿子.
- var input = document.getElementsByTagName('input');
- for(var i = 0;i < input.length;i++){
- // 当点击某个 input 时, 如果自己被选中, 此时改变父亲的颜色为绿色, 否则为白色
- input[i].onclick = function(){
- if(this.checked){
- this.parentNode.style.backgroundColor = 'green';
- }else{
- this.parentNode.style.backgroundColor = 'white';
- }
- }
- }
2.3 previousSibling 和 nextSibling 兄弟节点
上一个兄弟 previousSibling, 下一个兄弟 nextSibling. 同样的, 文本节点也属于节点, 注释也是节点, 所以一个节点的上一个兄弟可能是文本, 注释节点. 原生 JS 中没有提供类似 nextAll(),prevAll(),siblings()方法, 如果节点没有上一个兄弟或下一个兄弟, 返回 null.
console.log(pp.previousSibling.nodeType) console.log(pp.nextSibling.nodeType) |
- var pp = document.getElementById("pp");
- // 返回 obj 的前面一个兄弟
- function prevSibling(obj){
- // 开始遍历 obj 节点的前面, 直到遇见一个 nodeType 为 1 的节点
- var prev = obj;
- // 循环遍历. 注意 while 的条件是一个赋值语句! 赋值语句也有表达式的
- while(prev = prev.previousSibling){
- if(prev.nodeType == 1){
- return prev;
- }
- }
- return null;
- }
- // 得到真正的后面兄弟
- function nextSibling(obj){
- // 开始遍历 obj 节点的前面, 直到遇见一个 nodeType 为 1 的节点
- var next = obj;
- while(next = next.nextSibling){
- if(next.nodeType == 1){
- return next;
- }
- }
- return null;
- }
- // 返回 obj 的前面所有兄弟
- function prevAll(obj){
- // 开始遍历 obj 节点的前面, 直到遇见一个 nodeType 为 1 的节点
- var prev = obj;
- var arr = [];
- while(prev = prev.previousSibling){
- if(prev.nodeType == 1){
- arr.push(prev);
- }
- }
- return arr;
- }
- prevSibling(pp).style.background = "red";
- nextSibling(pp).style.background = "green";
- prevAll(pp)[1].style.background = "green";
三, 原生 JavaScript DOM 节点操作
HTML 节点我们原来最多就是改改 HTML 属性, 比如改改 src 属性; 或者改改 CSS 样式, 比如. style 或者. CSS().
现在的问题是, 我们要增加节点, 删除节点, 移动节点, 替换节点.
3.1 createElement()创建和 appendChild()添加
创建节点的方法: create 创建, Element 元素. 接收一个参数, 就是创建的标签是什么.
document.createElement() |
追加节点的方法: 创建出来的节点不在 DOM 树上, 所以就应该用 appendChild()来添加到 DOM 树上:
父亲. appendChild(新儿子); |
- var btn = document.getElementById('btn');
- var txt = document.getElementById('txt');
- var ul = document.getElementsByTagName('ul')[0];
- btn.onclick = function(){
- // 创建一个 li 标签, 用变量 oLi 来表示, 创建除了的节点不是任何节点的儿子(没有在 DOM 树上)
- var oLi = document.createElement('li');
- oLi.innerHTML = txt.value; // 改变这个节点的内容
- // 把新创建的节点, 追加到 DOM 树上
- ul.appendChild(oLi);
- }
appendChild()一般来说就是用来追加新创建的节点, 如果试图把页面上已经有的节点, appendChild()到别的地方, 那么这个节点将移动, 也就是说, 同一个节点不可能在页面上两个地方出现.
比如:
- <div id="box1">
- <p id="xiaoming">我是小明</p>
- </div>
- <div id="box2">
- </div>
- <script type="text/javascript">
- var box2 = document.getElementById('box2');
- var xiaoming = document.getElementById('xiaoming');
- box2.appendChild(xiaoming);
- </script>
以上将把 xioaming 移动到 box2 里面.
用 innerHTML 创建节点:
事实上, 工作的时候很少用 createElement. 因为 innerHTML 足够好用, innerHTML 也可以用来创建节点, 甚至效率 createElement 还高.
- var year = document.getElementById('year');
- for(var i = 1950; i <= 2018;i++){
- // 创建节点
- var op = document.createElement('option');
- // 改变创建出来的节点内容
- op.innerHTML = i;
- // 上 DOM 树
- year.appendChild(op); // 父亲. appendChild(新儿子);
- }
innerHTML 创建:
- for(var i = 1950; i <= 2018; i++) {
- year.innerHTML += '<option>'+i+'</option>';
- }
JavaScript 是动态变量:
- var oBox = document.getElementById('box');
- // 得到 box 里面所有的 p, 现在 box 没有 p, 所以 ops 是一个空数组
- var ops = oBox.getElementsByTagName('p');
- var np = document.createElement('p'); // 创建节点
- oBox.appendChild(np); // 追加节点
- var np = document.createElement('p'); // 创建节点
- oBox.appendChild(np); // 追加节点
- var np = document.createElement('p'); // 创建节点
- oBox.appendChild(np); // 追加节点
- var np = document.createElement('p'); // 创建节点
- oBox.appendChild(np); // 追加节点
- var np = document.createElement('p'); // 创建节点
- oBox.appendChild(np); // 追加节点
- // 这里弹出多少? 初学者认为弹出 0, 因为先得到数组 p, 然后创建节点, 节点又没有往数组里面 push, 所以应该 ops 数不变才对.
- // 但是 JS 中存储 DOM 节点的变量是动态, 不是一个瞬时照片, 而是一个有生命的动态对象, 当 oBox 里面的 p 标签变化时, ps 也变化
- console.log(ops.length)
3.2 insertBefore()添加
appendChild 是把新节点插入在父亲的所有子节点的后面, 也就是说添加的节点就是父亲的最后一个儿子.
我们可以在任意一个位置添加子节点: 会在原有标杆儿子之前插入
父亲. insertBefore(新儿子, 原有标杆儿子) |
- var btn = document.getElementById('btn');
- var txt = document.getElementById('txt');
- var ul = document.getElementsByTagName('ul')[0];
- var lis = document.getElementsByTagName('li');
- btn.onclick = function(){
- // 创建一个 li 标签, 用变量 oLi 来表示, 创建除了的节点不是任何节点的儿子(没有在 DOM 树上)
- var oLi = document.createElement('li');
- oLi.innerHTML = txt.value; // 改变这个节点的内容
- // 把新创建的节点, 追加到 DOM 树上
- // 在 lis[0]的前面插入
- ul.insertBefore(oLi,lis[0]);
- }
如果想每次都在开头添加, 那么就是:
ul.insertBefore(oLi,lis[0]); |
lis 这个变量是动态的, 这次添加的 li, 下回就是 lis[0].
3.3 removeChild()删除
父亲. removeChild(儿子);
- <ul>
- <li > 吃饭 <a href="###">删除</a></li>
- <li > 睡觉 <a href="###">删除</a></li>
- <li > 打豆豆 <a href="###">删除</a></li>
- </ul>
- <script type="text/javascript">
- var ul = document.getElementsByTagName('ul')[0];
- var lis = document.getElementsByTagName('li');
- var as = document.getElementsByTagName('a');
- for(var i = 0;i<as.length;i++){
- as[i].onclick = function(){
- ul.removeChild(this.parentNode);
- // 如果要自杀, 也要找到爸爸
- //this.parentNode.removeChild(this);
- }
- }
- </script>
如果要自杀, 也要找到爸爸:
this.parentNode.removeChild(this); |
3.4 replaceChild()替换
替换节点:
父亲. replaceChild(新儿子, 旧儿子) |
- <div>
- <p > 赵丽颖</p>
- <p id="xh">小黑</p>
- <p > 迪丽热巴</p>
- </div>
- <script type="text/javascript">
- var oBox = document.getElementsByTagName('div')[0]
- var xh = document.getElementById('xh');
- // 创建节点, 孤儿节点
- var op = document.createElement('p');
- op.innerHTML = '朱老师'; // 更改 op 的内容
- oBox.replaceChild(op,xh);
- </script>
3.5 clone()克隆
克隆节点, 参数 true 表示深度克隆, 节点里面的所有内容和事件一同复制.
复制之后的节点是个孤儿节点, 所以也要使用 appendChild()等方法来添加上 DOM 树.
克隆对象. cloneNode(true) |
- <div id="box1">
- <ul>
- <li><span > 赵丽颖</span></li>
- <li><span > 迪丽热巴</span></li>
- <li><span > 柳岩</span></li>
- <li><span > 志玲姐姐</span></li>
- </ul>
- </div>
- <div id="box2">
- </div>
- <script type="text/javascript">
- var box1 = document.getElementById('box1');
- var box2 = document.getElementById('box2');
- var ul = document.getElementsByTagName('ul')[0];
- var lis = document.getElementsByTagName('li');
- // 克隆 li 和 li 的所有后代(要加 true), 然后追加到 ul 中
- //ul.appendChild(lis[0].cloneNode()); // 克隆第 0 个 li
- box2.appendChild(ul.cloneNode(true)); // 克隆 ul 追加到 box2 中
- </script>
四, 事件监听
一堆理论知识正要来袭.
4.1 事件流
我们考虑一个结构, 三个 div 嵌套, 点击最内层的 div, 我们点击了谁? 仅仅点击了最内层 div 吗? 不是就像手指放在到一个同心圆中, 实际上手指触碰到了任何一个圆.
点击最内层的 div, 实际上浏览器会认为我们点击了所有的盒子, 甚至于 body,document,Windows.
为了描述事件的传播, 人为规定了一个事件的传播方向, 称为 "事件流". 两个阶段: 事件捕获阶段, 事件冒泡阶段.
"事件流" 描述的是页面上各个元素接收事件的顺序.
4.2 DOM0 级事件监听
DOM 分级别, DOM0 级, 1 级, 2 级, 3 级, 是不同的标准, 标准一直在升级.
之前学习的 on 开头的语法添加事件, 称为 "DOM0 级事件".
事件的触发一定是按照事件流的顺序, 由于 DOM0 级只能监听冒泡阶段, 所以顺序是: box3→box2→box1→body→document→Windows 如果改变监听顺序, 弹出顺序不变.
- box1.onclick = function(){
- alert('我是 box1');
- }
- box2.onclick = function(){
- alert('我是 box2');
- }
- box3.onclick = function(){
- alert('我是 box3');
- }
- document.body.onclick = function(){
- alert('我是 body')
- }
- document.onclick = function(){
- alert('我是 document')
- }
- Windows.onclick = function(){
- alert('我是 window')
- }
这种监听写法, 就是 DOM0 级, 就是把 onclick 当做属性添加给了 div 元素.
这种事件添加方法, 只能监听冒泡过程, 不能监听事件捕获阶段.
DOM0 级事件处理函数中, this 指的是触发事件的 DOM 元素, 就是事件传播到的这个元素.
DOM0 级事件处理函数中, 如果同一个对象, 同一个事件名, 绑定多个监听, 后面写的覆盖前面写的.
box1.onclick = function(){ alert('我是 box1');} box1.onclick = function(){ alert('我是 box1,后面写的');} |
DOM0 级事件, IE6,7 事件只能冒泡到 document,IE8 只能冒泡 body, 不能继续冒泡到 Windows. 也就是说不能给 Windows 对象添加事件.
4.3 DOM2 级事件监听
DOM1 级规范中, 没有对事件进行改动, 所以没有 DOM1 级的事情
DOM2 级做了新的规范, 不用 on** 来绑定监听了, 而是用一个方法
W3C 推出了 addEventListener()函数, add 添加, event 事件, listener 监听
它接收三个参数: 事件, 函数, 是否监听捕获阶段
元素. addEventListener(事件, 事件处理函数, 是否添加到捕获阶段) |
第一个参数: 事件名不用谢 on.(click,mouseover)
第二个参数: 函数可以是匿名函数, 也可以是有名函数
第三个参数: 布尔值, true 表示监听捕获, false 表示监听冒泡阶段
- box1.addEventListener('click',function(){
- alert('我是 box1 的捕获阶段')
- },true);
第三个参数是 true, 表示监听 box1 的捕获阶段的单击事件.
要记住 true 表示捕获, false 冒泡阶段: 口诀: true 上, false 下.
- box1.addEventListener('click',function(){
- alert('我是 box1 的捕获阶段')
- },true);
- box2.addEventListener('click',function(){
- alert('我是 box2 的捕获阶段')
- },true);
- box3.addEventListener('click',function(){
- alert('我是 box3 的捕获阶段')
- },true);
- box1.addEventListener('click',function(){
- alert('我是 box1 的冒泡阶段')
- },false);
- box2.addEventListener('click',function(){
- alert('我是 box2 的冒泡阶段')
- },false);
- box3.addEventListener('click',function(){
- alert('我是 box3 的冒泡阶段')
- },false);
box1 的捕获→box2 的捕获→box3 的捕获→box3 的冒泡→box2 的冒泡→box1 的冒泡
坑: 最内层的 box3, 谁先写就谁先执行, 也就是说对于最内层的 box3, 就不区分冒泡和捕获了, 对于不少罪内层的元素来说, 改变事件监听的顺序, 不会影响执行结果, 先捕获, 后冒泡.
DOM2 级事件中, 如果给同一个元素, 同一个事件名, 同一个阶段添加多个事件监听, 彼此不会覆盖, 先写的先执行.
- box1.addEventListener('click',function(){
- alert('我是 box1 的冒泡阶段 A')
- },false);
- box1.addEventListener('click',function(){
- alert('我是 box1 的冒泡阶段 B')
- },false);
- box1.addEventListener('click',function(){
- alert('我是 box1 的冒泡阶段 C')
- },false);
弹出顺序是: A→B→C
DOM2 级事件监听中, this 指的是触发事件的这个元素, 就是事件传播到的这个元素.
DOM2 级小测试 - 看看执行顺序: 正确答案在下篇文章.
小题目: 页面 box1 最大, 嵌套 box2,box3, 点击 box3 弹出顺序
- box2.onclick = function(){
- alert('A');
- }
- box2.onclick = function(){
- alert('B');
- }
- box2.addEventListener('click',function(){
- alert('C')
- },false);
- box2.addEventListener('click',function(){
- alert('D')
- },false);
- box2.addEventListener('click',function(){
- alert('E')
- },true);
- box2.addEventListener('click',function(){
- alert('F')
- },true);
- box3.addEventListener('click',function(){
- alert('G')
- },false);
- box3.onclick = function(){
- alert('H')
- }
- box3.addEventListener('click',function(){
- alert('I')
- },true);
4.4 低版本 IE 事件监听
IE6,7,8 不支持 addEventListener()方法, 支持 attachEvent().
- box1.attachEvent('onclick', function(){
- alert(1)
- });
第一个参数: 必须写 on, 和 addEventListener 不一样
第二个参数: 事件处理函数
没有第三个参数, 不能自由选择添加到什么阶段, 永远是冒泡阶段, 也就是说 IE678 不能监听捕获阶段.
- box1.attachEvent('onclick', function(){
- alert('我是 1a');
- });
- box1.attachEvent('onclick', function(){
- alert('我是 1c');
- });
- box1.attachEvent('onclick', function(){
- alert('我是 1b');
- });
- box2.attachEvent('onclick', function(){
- alert('我是 2');
- });
- box3.attachEvent('onclick', function(){
- alert('我是 3');
- });
比较搞笑, 如果给同一个元素添加多个事件名相同的监听, 不互相覆盖, 但是倒着执行, 先执行后写:
- box1.attachEvent('onclick', function(){
- alert('我是 a');
- });
- box1.attachEvent('onclick', function(){
- alert('我是 b');
- });
- box1.attachEvent('onclick', function(){
- alert('我是 c');
- });
弹出 c,b,a
最诡异是, this 永远是 Windows 对象而不是事件传播到的元素.
- box1.attachEvent('onclick', function(){
- alert(this === Windows);
- });
所以最正确的工作实践, 就是用 DOM0 级事件监听方法, 除非基于特别充分的理由, 否则不用 DOM2 级. 什么时候理由充分用 DOM2 级呢? 比如一些新兴事件, 如手机事件: touchstart,touchmove,touchend 事件等等. 只能用 DOM2 级.
上面学习的就是一些理论知识, 总结一下:
事件流永远是先下 (捕获) 后(冒泡), 但是不同监听方式, 决定了监听那一部分.
面试意义大于实际意义!
jQuery 中用的就是 DOM2 级, 也有轮子, jQuery 事件同名不会被覆盖.
4.5 事件监听移除
DOM0 级事件监听的移除很简单, 直接把事件属性赋值给 null 即可.
- var box1 = document.getElementById('box1');
- box1.onclick = function(){
- alert('你好, 再次点击就没有效果了');
- box1.onclick = null; // 移除事件监听
- }
DOM2 级事件监听移除, 必须指名道姓移除, 也就是说, 如果是匿名函数添加的事件监听, 是不可能去掉的.
添加的时候要有名字:
- box1.addEventListener('click',function fun(){
- alert('你好');
- box1.removeEventListener('click',fun,true);
- },true);
点击按钮后, 移除 box1 的事件监听:
- btn.onclick = function(){
- box1.removeEventListener('click',fun,true);
- }
IE6,7,8 用 detachEvent 即可, 同样, 也必须起名.
- box1.attachEvent('onclick',fun)
- function fun(){
- alert('你好');
- }
- btn.onclick = function(){
- box1.detachEvent('onclick',fun);
- }
五, 事件对象 event
这里的知识就不是理论了 , 有实战价值.
5.1 event 兼容性问题
在任何事件中, 都有事件对象 event. 浏览器会往事件处理函数中, 传入一个实参, 就是事件对象, 里面封装了你这次触发事件 (点击, 鼠标移入, 移出, 键盘按下) 的细节. 比如鼠标位置, 是否按下某个键等等.
一般用变量 event 来接收, 注意, 不是必须的, 名字可以改.
所有事件的细节, 都被封装到 event 对象里面.
比如 clinetX,clinetY 就是这次鼠标点击的位置.
先说兼容性问题, IE678 中, event 这事件对象不是实参, 而是全局变量, 系统会在每一个事件发生的时候, 都去改变这个全局变量的值:
- box.onclick = function(){ // 圆括号不能写 event, 如果写了, 就遮蔽了全局的 event
- alert(event.clientX)
- }
兼容写法:
- box.onclick = function(event){
- // 因为在 Chrome 中 event 是实参, IE678events 全局变量, 所以用或的短路语法, 它支持哪个用哪个
- var event = event || Windows.event;
- alert(event.clientX)
- }
5.2 事件对象属性
event.type 返回事件类型,没有 on,比如 “click” event.target 返回你点击的最小哪个元素,即使这个元素身上没有监听,也返回它 event.currentTarget 返回自己,this 一定和它是同一个元素,都是自己 event.bubbles 返回布尔值,表示这个事件是否冒泡 |
●bubbles 属性, 表示事件是否冒泡
- box1.onmouseover = function(event){
- var event = event || Windows.event;
- console.log(event.bubbles); // 表示是否冒泡
- }
基本上所有事件这个属性都是 true, 表示这个事件冒泡, 但是要记住:
onmouseover 的 event.bubbles 是 true,表示冒泡 onmouseout 的 event.bubbles 是 true,表示冒泡 onmouseenter 的 event.bubbles 是 false,表示不冒泡 |
有两个事件不冒泡:
onmouseenter 和 mouseleave 的 event.bubbles 是 false,不冒泡 |
onmouseenter 和 mouseleave 这两个事件 IE678910 全面兼容! 反而是 Chrome30 之前不兼容, 但是现在可以当做全面兼容. 它们两个事件是著名天生不冒泡的, 当一个内部元素鼠标进入了, 会执行这个元素的事件处理函数, 但是事件捕获继续往外层传播. 它的外层盒子不会触发 onmouseenter 事件.
- box1.onmouseenter = function(event){
- alert('鼠标进入了 box1')
- }
- box2.onmouseenter = function(event){
- alert('鼠标进入了 box2')
- }
- box1.onmouseout = function(event){
- alert('鼠标离开了 box1')
- }
- box2.onmouseout = function(event){
- alert('鼠标离开了 box2')
- }
onmouseenter 和 mouseleave 比 onmouseover 和 onmouseout 好用
- // $('#box').mouseenter(function(){
- // $(this).children('.mask').slideDown();
- // });
- // $('#box').mouseleave(function(){
- // $(this).children('.mask').slideUp();
- // });
- $('#box').mouseover(function(){
- $(this).children('.mask').slideDown();
- });
- $('#box').mouseout(function(){
- $(this).children('.mask').slideUp();
- });
●target,srcElement 属性, 表示事件发生的最内层元素
Chrome 同时支持下面 2 个属性, IE 只支持 srcElement:
var target = event.target || event.srcElement; |
用这个属性可以优化代码效率, 可以制作 "事件委托". 把子元素的事件, 委托给父亲, 父亲通过 event.target 来判断是谁触发了事件. 不使用事件委托, 我们将会有很多事件监听; 使用事件委托, 事件监听只有 1 个. 提升了页面的效率.
- box1.onclick = function(event){
- var event = event || Windows.event;
- var target = event.target || event.srcElement;
- alert(target.id)
- }
事件委托:
比如, 小圆点添加监听, 不要给所有 li, 直接给 ol, 让 ol 的 event.target 这个元素有 cur
- <body>
- <ol>
- <li class="cur">0</li>
- <li>1</li>
- <li>2</li>
- <li>3</li>
- <li>4</li>
- <li>5</li>
- </ol>
- </body>
- <script type="text/javascript" src="js/jquery-1.12.4.min.js"></script>
- <script type="text/javascript">
- $('ol').mouseover(function(event){
- var event = event || Windows.event;
- // 得到触发事件的最小元素
- var target = event.target || event.srcElement;
- // 让触发事件的元素添加 cur
- $(target).addClass('cur').siblings().removeClass('cur');
- });
- </script>
当页面上有大量的重复元素要添加监听的时候, 比如 100/200 个, 一定要注意事件委托.
ps: 尽量让它越来越规范, 前期的文章都是本人的学习时的笔记整理, 希望看完后可以指点一二, 提提意见多多交流;
笔记流程: HTML>CSS>JavaScript>jQuery>html5/css3 > 移动端 > Ajax > 面向对象 > canvas>Node.JS>es678>vue>react > 小程序>面试问题
意见请留言, 邮箱: scarf666@163.com
来源: https://www.cnblogs.com/rope/p/10610253.html