3

相信大家在DOM的实际开发与学习过程中,肯定也遇到不少需要比较的东西,这里我主要列比较以下几点,更多的区别和总结,希望想到和遇到的朋友给我留言哦。

  • clientHeight/scrollHeight/offsetHeight
  • defer vs async
  • 事件模型-捕获/目标/冒泡
  • 普通事件 vs 事件绑定
  • stopPropagation/preventDefault/return false
  • target/currentTarget

文中示例显示不是很好,可以直接去小弟博客看原文:DOM中的各种区别小节

各种height/width

CSS盒模型是比较复杂的,尤其是当页面中有滚动条时,仅仅通过css来操作高度宽度是不够的,幸运的是Javascript提供了不少这样的接口。Javascript中clientHeight / cliengWidthscrollHeight / scrollWidthoffsetHeight / offsetWidthheight / width 都可以获取高度和宽度,但是他们有一些细微的差别:

See the Pen 各种height/width区别 by superlin (@superlin) on CodePen.

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>
  • offsetHeight / offsetWidth:可见区域包含border,对于display:block的元素通过width/height + padding + border可以计算出来。
  • clientHeight / cliengWidth:可见区域包含padding,不包含border和滚动条,不能通过CSS样式计算出来,取决于滚动条的大小。
  • scrollHeight / scrollWidth:内容区域的大小,不包含border,包含不在可见区域内的隐藏部分,不能通过CSS样式计算出来。
  • height / width:不包含borderpadding

另外:滚动条的宽度可以通过如下方式计算:

javascriptscrollbarWidth = offsetWidth - clientWidth - getComputedStyle().borderLeftWidth - getComputedStyle().borderRightWidth

defer与async

<script>标签的deferasync属性大多数浏览器都已经支持了(IE9以下不支持defer),那么他们到底有什么区别呢?这里我们用几张图来描述,相信看完之后你就很清楚他们的区别了,首先对于图示做如下说明:

legend

<script>:如果script不带任何属性,这种情况下HTML文档遇到js脚本就会中止解析,先发送请求下载文件,下载完成后执行脚本,之后再继续解析HTML文档。

script

<script async>:带有async属性时,js脚本下载不会使HTML文档解析中止,下载完成后才会中止文档解析,执行完成后再继续解析文档。

async

<script defer>:带有defer属性时,js脚本下载不会使HTML文档解析中止,而且js执行都是在文档解析完成之后。

defer

一般情况下,能用async就用async,然后是defer,最后才是什么属性都不带,主要的规则如下:

  • 如果当前脚本是模块化的,而且不依赖其他脚本,使用async
  • 如果当前脚本依赖于其他脚本或被其他脚本所依赖,使用defer
  • 如果脚本文件很小,而且被其他async脚本依赖,可以将当前脚本作为内联脚本放在那些async脚本的前面

事件捕获与冒泡

先来看一个简单的问题,假设有一个element1元素,里面还有一个element2元素。

-----------------------------------
| element1                        |
|   -------------------------     |
|   |element2               |     |
|   -------------------------     |
|                                 |
-----------------------------------

这两个元素都绑定了click事件,如果用户点击element2,那个先触发呢,换句话说事件触发的的顺序是怎样的呢?

  • 网景公司说element1优先,这叫事件捕获(event capturing)
  • 微软则坚持element2优先,这叫事件冒泡(event bubbling)

首先来看看事件捕获:

               | |
---------------| |-----------------
| element1     | |                |
|   -----------| |-----------     |
|   |element2  \ /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------

可以看出element1上的事件处理函数先触发,element2上的事件处理函数后触发。

那么事件冒泡又是怎样的呢:

                / \
 ---------------| |-----------------
 | element1     | |                |
 |   -----------| |-----------     |
 |   |element2  | |          |     |
 |   -------------------------     |
 |        Event BUBBLING           |
 -----------------------------------

这种情况下element2上面的事件处理函数先触发,element1上面的事件处理函数后触发。

对于这两种模型的截然不同,W3C很巧妙地在这场对抗中保持中立:任何W3C事件模型中发生的事件都是先捕获,直到它到达目标元素,然后再向外冒泡

                 | |  / \
-----------------| |--| |-----------------
| element1       | |  | |                |
|   -------------| |--| |-----------     |
|   |element2    \ /  | |          |     |
|   --------------------------------     |
|        W3C event model                 |
------------------------------------------

基于事件模型可以有很多应用,最常见的就是事件代理和委托,感兴趣的可以深入研究,这里我不在具体描述。

普通事件与事件绑定

普通事件就是on+event绑定的事件,Javascript中有很多定义好的事件,例如 onclickonkeyuponmouseup 等,这种方式的使用示例如下:

javascriptobj1.onclick = function1;
obj1.onclick = function2;

以上的操作下,function1会被function2覆盖而只执行function2,解除事件只需:

javascriptobj1.onclick = null;

使用支持W3C标准的浏览器中绑定事件用的是addEventListener:

javascriptobj1.addEventListener("click",change1,false);
obj1.addEventListener("click",change2,false);

事件执行顺序跟绑定顺序一样,先执行change1,再执行change2,解除绑定:

javascriptobj1.removeEventListener("click",change1,false);

在IE里面,绑定事件要用attachEvent:

javascriptobj1.attachEvent("onclick",change1);
obj1.attachEvent("onclick",change2);

绑定时事件名称同样要以on为前缀,而且没有后面是否冒泡的boolean值,但是这种执行顺序变成了倒序,先执行change2,再执行change1。 事件取消绑定:

javascriptobj1.detachEvent("onclick",change1);

stopPropagation,preventDefault和return false

因为有父, 子节点同在, 因为有监听事件和浏览器默认动作之分. 使用 JavaScript 时为了达到预期效果经常需要阻止事件和动作执行. 一般我们会用到三种方法, 分别是 stopPropagation(), preventDefault()return false。那么他们有什么区别呢?

See the Pen stopPropagation,preventDefault和return false对比 by superlin (@superlin) on CodePen.

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

stopPropagation:因为事件可以在各层级的节点中传递, 不管是冒泡还是捕获, 有时我们希望事件在特定节点执行完之后不再传递, 可以使用事件对象的 stopPropagation 方法。

preventDefault:元素上带有的功能. 如: 点击 a 链接节点的跳转动作, 点击submit按钮表单会提交等,如果监听这些元素的事件时不希望默认动作方式,就可以使用 preventDefault 方法。

return false:退出执行, return false 之后的所有触发事件和动作都不会被执行. 有时候 return false 可以用来替代 stopPropagationpreventDefault,除此之外,还可以返回对象, 跳出循环等。

可以去上面的例子试试哦。

target与currentTarget

target在事件流的目标阶段;currentTarget在事件流的捕获,目标及冒泡阶段。只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的对象而currentTarget指向当前事件活动的对象(一般为父级),具体示例如下。

See the Pen target与currentTarget区别 by superlin (@superlin) on CodePen.

<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

更多的例子和意见请给我留言。

欢迎光临小弟博客:Superlin's Blog
我的博客原文:DOM中的各种区别小节

参考


lwl
1.5k 声望39 粉丝

The reason why a great man is great is that he resolves to be a great man.