jQuery之text()的實現

進擊的小進進發表於2019-04-03

jQuery之text()的實現

一、有這樣一段 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 部分原始碼見:www.jianshu.com/p/645b3b446…

也就是說:呼叫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 標籤!

參考: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>
複製程式碼

jQuery之text()的實現

(完)

相關文章