说起这个话题要从一个面试题开始,前两天一个朋友发给我的(某团面试题),这个题简单的很,但是估计很多人不能自信的说出结果和原因。
先看下题目
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .box{ width: 400px; border:solid 1px #000; } img{ width: 50%; } </style> </head> <body> <div class="box"> <img src="../img/g.jpg"/> <img src="../img/g.jpg" /> <img src="../img/g.jpg" /> </div> </body> </html>
请说出上面代码的显示结果,然后说下原因。
emm.....
其实这个布局的最终目的是想让图片并排显示,但是这样写并不能达到我们想要的结果,还是从细节中考查我们对 DOM 的理解和应用。
问题分析
当然做题是次要的,我们来分析下原因。
先看下效果,下面这个结果应该和你想的差不多吧。
这是什么原因呢?我们都知道 img
标签默认是内联元素,除非剩余宽度不足才会换行,代码中我们都设置成为50%
,宽度也没有超出呀。
这是因为html
中标签后的每个换行也会被当做是一个节点,会进行渲染,也会占用空间,这个节点就是text
节点,他不像标签那么形象,可以有具体的表示,他就是换行和空字符串的组合。
既然他是存在的,那我们使用 js
拿到这些节点。
document.getElementsByClassName('box')[0].children
上面的代码应该没什么问题,但是结果里没有我们所说的 text
节点。
请看下MDN
里这个方法的说明
ParentNode.children
是一个只读属性,返回 一个Node
的子elements
,是一个动态更新的HTMLCollection
。
也就是说children
属性返回的是elements
,然而 text node
属于Node
,所以没有返回。
所以我们需要使用另外一个方法得到
document.getElementsByClassName('box')[0].childNodes
上面的结果的确得到了text node
,但是同时也把 img
返回了。
element 和 node 关系
可能你会问 element
和node
到底什么区别呢?
一图胜千言
从上图中可以很清晰的看到他们的关系,越往上越抽象,越往下越具体,从继承的角度来说Element
继承自Node
,具有node的方法,同时扩展了很多自己的独有的方法。
所以在Element的一些方法里,是明确区分了Node和Element的,比如说:childNodes, children, parentNode, parentElement等方法。
另外Node表示的是DOM树的结构,在html中,节点与节点之间是可以插入文本的,这个插入的空隙就是TEXT_NODE,也就是我们上题中的情景。
所以总结来说,我们可以把所有的element
看作是 node
,但是所有的 node
并不是 element
。
回到题中,我们让 text node
显示出来。
给所有的 text node
设置文本
document.getElementsByClassName('box')[0].childNodes.forEach(item=>{ item.nodeType===3?item.appendData('text node'):null })
处理问题
说到这里,我觉得如何解决问题已经不重要了,如果能分析出来,自然就有了解决方法。
最直接的手段就是删除掉 text node。
<div class="box"><img src="../img/g.jpg"/><img src="../img/g.jpg" /><img src="../img/g.jpg" /></div>
搞定!
但这可读性太差了,看着多别扭。
设置字号为0.font-size:0
。
最后也不一定能过
如果你的回答只说会换行显示,这个也不能说是100%对,因为还少说了一项。
这个空隙呢?
很熟悉吧。
到这里可能面试官就会给过了。好难...ಥ_ಥ