你可能知道怎麼解決這個問題,可是你真的瞭解背後的緣由嗎?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
元素之間有空隙?
不記得了。
問題結束。