jQuery之text()的实现

39次阅读

共计 6364 个字符,预计需要花费 16 分钟才能阅读完成。

一、有这样一段 html
<div class=”divOne”>
<p> 嘿嘿嘿 </p>
</div>
<div class=”divOne”>
<p> 哈哈哈 </p>
</div>

二、jQuery 的 text() 方法
(1)当直接调用 $().text() 时,.text() 的作用是(循环)读取(多个)目标元素的 textContent/nodeValue
简单实现:
function readText(elem) {
let node,
ret = “”,
i = 0,
nodeType = elem.nodeType
console.log(nodeType,’nodeType22′)
// 如果 selector 是类的话,会有多个目标元素,此时需要分别单个循环
// 比如 document.querySelectorAll(‘.divOne’).nodeType ->undefined
if (!nodeType) {
while ((node = elem[i++])) {
// 单个获取
ret += readText(node)
}
}
// 元素节点,文档节点,文档碎片
else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {
// 如果目标元素的内容是文本,则直接返回
if (typeof elem.textContent === “string”) {
/*jQuery 没有用 innerText 获取文本的值,http://bugs.jquery.com/ticket/11153,
大概就是在 IE8 中新节点插入会保留所有回车。
所以 jQuery 采用了 textContent 获取文本值,
textContent 本身是 dom3 规范的,可以兼容火狐下的 innerText 问题。*/
return elem.textContent
}
// 如果节点内容不是文本,则循环子节点,并依次获取它们的文本节点
else {
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
ret += readText(elem)
}
}
}
// 文本节点、一个文档的 CDATA 部分(没遇到过这个)
else if (nodeType === 3 || nodeType === 4) {
// 返回节点值
return elem.nodeValue;
}
//nodeType:注释节点 8,处理指令 7
//text() 方法不处理这两个类型节点
return ret
}
(2)当调用 $().text(value) 时,.text(value) 的作用是为每一个符合条件的目标元素的 textContent 设置为 value
简单实现:
writeText():
function writeText(value) {
let elem,
i = 0;
// 先清空目标元素的内容
customEmpty.call(this)
// 循环
for (; (elem = this[i]) != null; i++) {
// 元素节点,文档碎片,文档节点
if (elem.nodeType === 1 || elem.nodeType === 11 || elem.nodeType === 9) {
// text() 方法不会解析标签
elem.textContent = value;
}
}
//return this 方便链式调用
return this
}
customEmpty():
function customEmpty() {
let elem,
i = 0;
// 注意 for 循环的写法
for (; (elem = this[i]) != null; i++) {
// 如果是元素节点的话,清空该节点的所有内容
if (elem.nodeType === 1) {
elem.textContent = “”;
}
}
return this;
}
(3)源码实现
源码:
jQuery.text() 总体:
// 源码 6152 行
text: function(value) {
return access(this, function( value) {
return value === undefined ?
// 读
// 如果直接调用 text() 的话, 就调用 Sizzle.getText
jQuery.text(this) :
// 写
// 循环
this.empty().each( function() {
// 先清空目标元素的内容,然后再赋值
if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
console.log(value,’value6159′)
// 如果包含标签的话,需要用 html() 方法,text() 方法不会解析标签
/*jQuery 没有用 innerText 获取文本的值,http://bugs.jquery.com/ticket/11153,
大概就是在 IE8 中新节点插入会保留所有回车。
所以 jQuery 采用了 textContent 获取文本值,
textContent 本身是 dom3 规范的,可以兼容火狐下的 innerText 问题。*/
this.textContent = value;
}
} )
}, null, value, arguments.length );
},
源码解析:
① 调用 text(),实际上是调用 access()access 部分源码见:https://www.jianshu.com/p/645b3b4461c5
也就是说:调用 jQuery.access() 相当于调用了 fn.call( elems, value),即自定义的方法 jQuery.access(this, function(value) {xxx})
② .text() 的情况调用这部分源码:
jQuery.text() 调用的其实是 Sizzle.getText():
// 源码 2833 行
jQuery.text = Sizzle.getText;
Sizzle.getText():
// 源码 1642 行
getText = Sizzle.getText = function(elem) {
var node,
ret = “”,
i = 0,
nodeType = elem.nodeType;

if (!nodeType) {
while ((node = elem[i++]) ) {
// Do not traverse comment nodes
ret += getText(node);
}
}
// 元素节点、文档节点、文档碎片
else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (jQuery #11153)
// 如果目标元素的子节点是文本节点,则直接返回它的 textContent
if (typeof elem.textContent === “string”) {
/*jQuery 没有用 innerText 获取文本的值,http://bugs.jquery.com/ticket/11153,
大概就是在 IE8 中新节点插入会保留所有回车。
所以 jQuery 采用了 textContent 获取文本值,
textContent 本身是 dom3 规范的,可以兼容火狐下的 innerText 问题。*/
return elem.textContent;
}
// 如果子节点不是文本节点,则循环子节点,并依次获取它们的文本节点
else {
// Traverse its children
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
ret += getText(elem);
}
}
}
// 文本节点、一个文档的 CDATA 部分(没遇到过这个)
else if (nodeType === 3 || nodeType === 4) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
return ret;
};
③ .text(value) 的情况调用这部分源码:
jQuery.text(value):
// 写
// 循环
this.empty().each( function() {
// 先清空目标元素的内容,然后再赋值
if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
console.log(value,’value6159′)
// 如果包含标签的话,需要用 html() 方法,text() 方法不会解析标签
/*jQuery 没有用 innerText 获取文本的值,http://bugs.jquery.com/ticket/11153,
大概就是在 IE8 中新节点插入会保留所有回车。
所以 jQuery 采用了 textContent 获取文本值,
textContent 本身是 dom3 规范的,可以兼容火狐下的 innerText 问题。*/
this.textContent = value;
}
} )
empty():
// 源码 6231 行
empty: function() {
var elem,
i = 0;

for (; ( elem = this[ i] ) != null; i++ ) {
// 如果是元素节点的话
if (elem.nodeType === 1) {
// Prevent memory leaks
// 清空内容和事件,防止内存泄漏
jQuery.cleanData(getAll( elem, false) );
// Remove any remaining nodes
// 清空节点所有内容
elem.textContent = “”;
}
}
return this;
},
④ 总结

$(“.divOne”).text() 的本质:
(1)节点内容是文本,返回 $(“.divOne”)[i].textContent
(2)节点内容不是文本,循环返回 $(“.divOne”)[i].element[j].textContent
(3)节点内容是文本节点或一个文档的 CDATA 部分, 则返回 $(“.divOne”)[i]. nodeValue

$(“.divOne”).text(“Hello <b>world</b>!”) 的本质:
(1)jQuery.cleanData()
(2)$(“.divOne”)[i].textContent = “”
(3)$(“.divOne”)[i].textContent=”Hello world!”
注意:text() 不会去解析 html 标签!
参考:http://api.jquery.com/text/
完整代码:
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<title>jQuery 之 text()</title>
</head>
<body>
<script src=”jQuery.js”></script>
<div class=”divOne”>
<!–<p id=”divTwo”> 嘿嘿嘿 </p>–>
<p> 嘿嘿嘿 </p>
</div>
<div class=”divOne”>
<p> 哈哈哈 </p>
</div>
<input type=”text” id=”inputOne”>
<script>
function readText(elem) {
let node,
ret = “”,
i = 0,
nodeType = elem.nodeType
console.log(nodeType,’nodeType22′)
// 如果 selector 是类的话,会有多个目标元素,此时需要分别单个循环
// 比如 document.querySelectorAll(‘.divOne’).nodeType ->undefined
if (!nodeType) {
while ((node = elem[i++])) {
// 单个获取
ret += readText(node)
}
}
// 元素节点,文档节点,文档碎片
else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {
// 如果目标元素的内容是文本,则直接返回
if (typeof elem.textContent === “string”) {
/*jQuery 没有用 innerText 获取文本的值,http://bugs.jquery.com/ticket/11153,
大概就是在 IE8 中新节点插入会保留所有回车。
所以 jQuery 采用了 textContent 获取文本值,
textContent 本身是 dom3 规范的,可以兼容火狐下的 innerText 问题。*/
return elem.textContent
}
// 如果节点的内容不是文本,则循环子节点,并依次获取它们的文本节点
else {
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
ret += readText(elem)
}
}
}
// 文本节点、一个文档的 CDATA 部分(没遇到过这个)
else if (nodeType === 3 || nodeType === 4) {
// 返回节点值
return elem.nodeValue;
}
//nodeType:注释节点 8,处理指令 7
//text() 方法不处理这两个类型节点
return ret
}

function customEmpty() {
let elem,
i = 0;
// 注意 for 循环的写法
for (; (elem = this[i]) != null; i++) {
// 如果是元素节点的话,清空该节点的所有内容
if (elem.nodeType === 1) {
elem.textContent = “”;
}
}
return this;
}

function writeText(value) {
let elem,
i = 0;
// 先清空目标元素的内容
customEmpty.call(this)
// 循环
for (; (elem = this[i]) != null; i++) {
// 元素节点,文档碎片,文档节点
if (elem.nodeType === 1 || elem.nodeType === 11 || elem.nodeType === 9) {
// text() 方法不会解析标签
elem.textContent = value;
}
}
//return this 方便链式调用
return this
}

function customText(value) {
return value === undefined ?
// 读
readText(this) :
// 写
writeText.call(this, value)
}

customText.call(document.querySelectorAll(‘.divOne’))
customText.call(document.querySelectorAll(‘.divOne’),”Hello <b>world</b>!”)
// let p=document.createElement(‘p’)
// p.innerText=’ 哈哈哈 ’
console.log($(“.divOne”).text())
// customText.call(document.querySelectorAll(‘.divOne’))
// console.log(document.querySelectorAll(‘.divOne’).nodeType,’childnode81′)
// console.log(document.querySelectorAll(‘.divOne’)[0].textContent,’childnode81′)
// $(“#divOne”).text(‘<p>aaaa</p>’)
// console.log(document.querySelector(“#divTwo”))

</script>
</body>
</html>

(完)

正文完
 0