Qatrix 源码阅读 2

Manipulation

这里只说一个方法 $text。

该方法用于获取或设置元素的文本。本来该方法和 $html 应该是同样的代码量,毕竟用法和功能类似,但是看看源码,比 $html 多出了两倍不止,为什么会这样?

我们知道获取元素的 HTML 代码用 innerHTML(),本来获取元素的文本也应该是 innerText(),但是 Firefox 使用了另一个名字:textContent。到后来,textContent 成了标准,IE 9 开始支持该方法。

这两个方法可不仅仅是名字上的区别,参见 MDN 关于两者的区别介绍:

1,textContent 会获取所有元素的内容,包括 <script> 和 <style> 元素,但是 innerText 不会。

2,innerText 会受到样式的影响,它不会返回隐藏元素的文本,但 textContent 会。

3,由于 innerText 受到 CSS 样式影响,因此它会触发 reflow,但 textContent 不会。

由于以上存在的区别(尤其是第一,二条),所以 Qatrix 自己实现了获取元素内容的方法。

让我们略过 mapcall 方法调用,来看源码:

if (text) {
	// Set text node.
	$empty(elem);
	elem.appendChild(document.createTextNode(text));

	return elem;
}

当 text 参数传入,表明是设置元素的文本,先使用 $empty 清空元素,然后再添加文本节点。

else {
	var rtext = '',
		textContent = elem.textContent,
		nodeType;

	// 如果元素的内容只是文本
	if ((textContent || elem.innerText) === elem.innerHTML) {
		rtext = textContent ? $string.trim(elem.textContent.replace(rbline, '')) : elem.innerText.replace(rline, '');
	}
	else {
		for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
			nodeType = elem.nodeType;

			if (nodeType === 3 && $string.trim(elem.nodeValue) !== '') {
				rtext += elem.nodeValue.replace(rbline, '') + (elem.nextSibling && elem.nextSibling.tagName && elem.nextSibling.tagName.toLowerCase() !== 'br' ? "\n" : '');
			}

			if (nodeType === 1 || nodeType === 2) {
				rtext += $text(elem) + ($style.get(elem, 'display') === 'block' || elem.tagName.toLowerCase() === 'br' ? "\n" : '');
			}
		}
	}

	return rtext;
}

该段内容里有一个 if else 分支,当元素的内容仅包含文本时,使用元素支持的获取文本的方法来调用。因为 innerHTML 没有 innerText 的那些限制,因此如果元素是 script、style 或是隐藏的元素,那么 innerHTML 和 innerText 获取的值必然不同,就不会进入该分支。

在下一个分支里,开始手动循环元素的子节点。

如果子节点是文本节点,即 nodeType 为 3,去掉换行,但接下来的判断我搞不太懂了,下一个元素不是 br 的话,什么要生成一个换行呢?

如果子节点是元素节点或属性节点,则递归调用 $text 来处理子节点下的子节点。在拼接字符串时判断了元素的 display 是否为 block,因为块状元素会产生换行,所以这里把它和 br 元素一并处理。