你可能知道怎么解决这个问题,可是你真的了解背后的缘由吗?javascript
请查看下面的示例代码,在ul
元素下有三个使用display: inline-block;
水平排列的li
元素。css
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Demo</title>
<style> * { margin: 0; padding: 0; } #root { width: 400px; border: 3px solid #7abdb6; margin: 40px; padding: 20px; } ul > li { display: inline-block; padding: 5px; background: #27a3ff; color: aliceblue; } </style>
</head>
<body>
<div id="root">
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
</div>
</body>
</html>
复制代码
请问,为何li
元素之间有空隙?html
经过使用document.querySelector('ul').childNodes
获取ul
元素的子节点,能够发现,在li
元素之间,存在节点类型为3
的文本节点text
,空隙即文本节点占据的空间。vue
那如何去除li
元素之间的空隙呢?java
删除文本节点便可,好比咱们能够换成这种写法:node
<div id="root">
<ul><li>A</li><li>B</li><li>C</li></ul>
</div>
复制代码
很好,可是这样代码可读性会下降,有更优雅的方式吗?git
可使用HTML
解析器,自动删除空白字符文本节点。例如,在vue
项目中,咱们能够这么写:github
<div id="root">
<div id="app"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script> (function () { const res = Vue.compile(` <ul> <li>A</li> <li>B</li> <li>C</li> </ul> `, { whitespace: 'condense' }); new Vue({ el: '#app', render: res.render, staticRenderFns: res.staticRenderFns, }); }()); </script>
复制代码
删除空白字符可能会形成误伤,有不删除空白字符的方法吗?面试
能够给ul
设置font-size: 0;
,让空白字符不占据空间,例如:npm
ul {
font-size: 0;
}
li {
font-size: initial;
}
复制代码
虽然默认状况下,Chrome
只容许设置最小12px
字号,可是设置0px
也是容许的。
若是从布局的角度来考虑,还有其它解决方案吗?
有的,可使用浮动布局,例如:
ul:after {
content: ' ';
display: block;
clear: both;
height: 0;
visibility: hidden;
}
li {
float: left;
}
复制代码
或者使用弹性盒子布局也能够:
ul {
display: flex;
}
复制代码
那为何使用float
或者flex
以后,li
之间的空白字符就不占据空间了呢?
咱们换一个更简单的示例:
<p>
<span>A</span>
<span>B</span>
</p>
<p>
<span>C</span><span>D</span>
</p>
复制代码
能够发现,在浏览器渲染后,A
与B
之间存在空隙,而C
与D
是牢牢挨着的。
让咱们来回顾一下人类语言。
对于中文(也包括一些其它的语言)而言,词与词之间是直接相连的。
JavaScript是世界上最好的语言。
可是对于英文(也包括一些其它的语言)而言,词与词之间是使用空格分隔的。
JavaScript is the best language in the world.
因此,浏览器在渲染包括文本在内的行内元素时,为了保证语义正确性,必需保留渲染空白字符。
也就是说,li
之间有空隙,本质上是由于display: inline-block;
将它们转换为了行内元素。因此浏览器在渲染它们时,选择的是和文本渲染相同的策略,把每一个li
当成了一个单词来对待,这样它们之间的空格天然也就保留渲染了。而使用float
就意味着使用块布局,它会在某些状况下修改display
属性的计算值。因此,此时li
的display
属性虽然表面上仍是inline-block
,可是实际上已经被修改成了block
。对于flex
也是一样的道理。
回到最开始,ul
下有7个子节点,为何第1个和最后1个文本节点没有占据空间呢?
console.log(document.querySelector('ul > li:first-child').getBoundingClientRect());
// DOMRect {x: 63, y: 63, width: 30.515625, height: 32, top: 63, right: 93.515625, bottom: 95, left: 63}
复制代码
能够看到,第1个li
元素的x
为63,正好等于margin 40px + border 3px + padding 20px
。
一样用一个更简单的示例:
<div>
Here is an English paragraph
that is broken into multiple lines
in the source code so that it can
more easily read in a text editor.
</div>
复制代码
能够发现,这一大段文本都渲染在了同一行。
回到互联网刚诞生的时候,网页都是纯静态的,因此网页里面的内容都是直接硬编码出来的,而不像今天咱们大多都是动态渲染或静态编译出来的。这样,为了HTML
源码的可读性和易维护性,空格、缩进和换行,以及一些其它空白字符就会被大量使用了。可是浏览器在渲染的时候,不能把这些都渲染出来啊,因此就有了一个折叠连续空白字符的概念。
简言之,就是把连续的空白字符所有折叠为单个的空白字符,并尽可能不渲染空白字符。
这里直接引用CSS
规范里面的一段原文。
重点在第一、第3和第4点,行首和行尾的连续可折叠的空白字符在渲染时将被删除,若是行尾仍有可折叠的空白字符,则悬挂到行首。
因此,能够看到,第1个文本节点是没法选中的,可是第7个文本节点能够被选中,且出如今了第1个li
的前面!
注意:这里的第7个文本节点能够被选中且宽度为0,是受到了第5个文本节点的影响。
若是换成下面的写法:
<ul>
<li>A</li><li>B</li><li>C</li>
</ul>
复制代码
则最后一个文本节点一样没法被选中。
某宇宙公司面试官发了个 codepen 上的源码连接。
请问,为何div
元素之间有空隙?
不记得了。
问题结束。