文章目录
一、正确认识 “空隙”(空白符)
1. 你在使用 inline、inline-block 元素时,是否经常遇到过这个问题?
明明没有设置 margin、border 等样式,为何会出现这个"空隙"?难道出"BUG"了吗?
不!这是正常现象,也是合理存在的。并不是什么"BUG"!
哪既然不是"BUG",那是什么?应该如何解决?
看官莫急,咱们来具体问题具体分析具体解决。
2. 为何会产生"空隙"?
首先要知道,<body> 是网页的主体部分,也就是用户可以看到的内容。而我们在编辑器中编写的被 < 和 > 包裹的部分,会被浏览器解释为HTML的标记符(开始标签与结束标签),是不会被当作正文显示出来的。
如编辑器中:<div>文本内容</div>
用户可看到:文本内容
也就是说,除了开始标签与结束标签,其余字符,都会被当作正文处理,即文本内容(这在 DOM 节点中称为 “文本节点”)。所以,你在 body 内部编写的代码,除了标记符(起始标签、结束标签),其余你敲的字符都被显示了出来,包括——空白符(回车换行、空格…)
- 敲代码过程中,为了换行,敲下的回车键:
- 敲代码过程中,为了隔开,敲下的空格键:
- 其余情况:
当然了,无论是 block,还是 inline、inline-block,遇到以上情况都会产生空白符,但是显示出空隙的现象仅仅存在于相邻的行内元素中,块级元素则无此现象,但是这些空白符还是可以通过 DOM 的节点操作进行获取的,这个知识点将在第二部分进行说明。
3. 如何消除这些"空隙"?
- 方法①:行内元素的标签无缝衔接
- 方法②:注释大法
- 方法③:设置 CSS 样式
- 方法④:通过 DOM 操作来删除空白节点(但缺点还是有的,第二部分会详写)
-
注意,在以上解决方法中:
- 第一种方法,衔接处不会产生任何节点;
- 第二种方法,衔接处会产生注释节点;
- 第三种方法,虽然空白符的字体尺寸被渲染为 0,但实际上该节点还是可以被 DOM 捕捉到,即空白的文本节点;
- 第四种方法,通过 DOM 删除的空白节点,不会被显示或捕捉到,但同时,span外部的文字内容也一并被删除了;
4. 题外补充
- 除了字符实体,其余连续的多个空字符(即使是换行符加空白符),都会被浏览器压缩为 1 个显示。
- 每个文字,都是特殊的"内联元素"。相当于每个字都被 <span> 标签包裹着:
注意1:文字不是元素,它们互为兄弟节点,同时也是父元素的子节点。此处只是类比而已。
注意2:实际上每个文字并不是被 span 包裹。它们实际上可以为兄弟节点或父子节点。此处只是通过另一个角度去说明而已。
二、通过 DOM 节点来认识"空隙"(空白节点)
1. DOM节点相关知识
在 HTML DOM (Document Object Model) 中 , 每一个元素都是节点:
- 文档是一个文档节点。
- 所有的HTML元素都是元素节点。
- 所有 HTML 属性都是属性节点。
- 文本插入到 HTML 元素是文本节点。
- 注释是注释节点。
2. 文本节点的获取
-
先来看看下面段落p的节点获取情况:
-
让后把段落p的文本节点的内容换成空白符:
【从上面两个 demo 中,你会发现,空白符也是文本节点,称之为"空白文本节点",它们的长度,是可以获取的。】</font>
-
接着,我们换成平时写代码的格式,再来获取看看:
-
不懂?那就用 encodeURI() 对节点的值进行编码,再来观察看看:
从上面几个例子中可知,我们在编写代码过程中,敲下的回车、空格、tab等,都被浏览器以"内容为空白符的文本内容"显示出来。
在 《1-3.如何消除这些"空隙"?(点击前往)》中,有讲到4种解决方法。前三种不再作详细说明,下面会针对 DOM 删除节点方法进行详细解释。
3. 遍历删除空白节点的操作,会连同其他非空白文本也一并删除掉
空白符与文字内容同属于文本节点,所以,如此删除空白节点,会连同其余非空白符的内容也一并删除了。
完善方法是,通过正则判断是否为纯空白节点,是则删除,不是则保留(即默认格式);
4. 题外补充
IE8及以下版本,在块元素的子元素也都是块元素的情况下,不会算入空白节点,但是子元素有内联元素则另说了(怪异的)。
三、函数封装
1. 无参,直接运行,删除 body(含)内所有元素的空白节点
/**
* 提示:
* 1. 该脚本,只针对纯空白符文本节点进行删除,若含其他文本内容则该节点不删除。
* 2. 直接执行即可,该脚本针对 body 及内部所有元素的空白子节点进行删除,不影响 head 部分。
*/
// 移除body及其所有子元素标签衔接处的空白节点
window.onload = function() {
;(function(doc) {
// 为了兼容 IE,使用获取标签名的方法,获取所有元素
var allEle = doc.getElementsByTagName('*');
// 判断该元素是否 body 内(含body)元素
function isInBody(ele) {
if (ele.nodeName.toLowerCase() === 'html' || ele.nodeName.toLowerCase() === 'head'
|| (ele.nodeName.toLowerCase() === 'title'
|| ele.nodeName.toLowerCase() === 'base'
|| ele.nodeName.toLowerCase() === 'link'
|| ele.nodeName.toLowerCase() === 'meta'
|| ele.nodeName.toLowerCase() === 'style'
|| ele.nodeName.toLowerCase() === 'script')
&& ele.parentNode.nodeName.toLowerCase() === 'head') { // 区分head与body内的同名标签
return false;
} else { // 剩下的是 body 以及其内部所有子元素
return true;
}
}
// 移除纯空白符文本节点
var removeWhiteSpace = (function(ele) {
// 遍历标签集
for (var i = 0; i < ele.length; i++) {
// 仅对 body 及其内部所有相邻子元素间的空白节点进行删除,不会影响到元素内部的文本节点内容
if (isInBody(ele[i])) {
// 匹配非空白符
var reg = /\S/;
// 遍历每个标签的子节点
for (var j = 0; j < ele[i].childNodes.length; j++) {
var str = ele[i].childNodes[j].nodeValue;
// 若为文本节点,且只能匹配到空白符,则删除
if (ele[i].childNodes[j].nodeType === 3 && !reg.test(str)) {
ele[i].removeChild(ele[i].childNodes[j]);
}
}
}
}
})(allEle);
})(document);
}
2. 传参,删除指定元素的子节点
// 移除纯空白符文本节点
function removeWhiteSpace(ele) {
// 匹配非空白符
var reg = /\S/;
// 遍历标签内的子节点
for (var i = 0; i < ele.childNodes.length; i++) {
var str = ele.childNodes[i].nodeValue;
// 若为文本节点,且只能匹配到空白符,则删除
if (ele.childNodes[i].nodeType === 3 && !reg.test(str)) {
ele.removeChild(ele.childNodes[i]);
}
}
}