亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

Jquery-1.9.1源碼分析系列(十一)之DOM操作

2019-11-20 11:10:25
字體:
來源:轉載
供稿:網友

DOM操作包括append、prepend、before、after、replaceWith、appendTo、prependTo、insertBefore、insertAfter、replaceAll。其核心處理函數是domManip。

  DOM操作函數中后五種方法使用的依然是前面五種方法,源碼

jQuery.each({    appendTo: "append",    prependTo: "prepend",    insertBefore: "before",    insertAfter: "after",    replaceAll: "replaceWith"  }, function( name, original ) {    jQuery.fn[ name ] = function( selector ) {      var elems,      i = 0,      ret = [],      insert = jQuery( selector ),      last = insert.length - 1;      for ( ; i <= last; i++ ) {        elems = i === last ? this : this.clone(true);        jQuery( insert[i] )[ original ]( elems );        //現代瀏覽器調用apply會把jQuery對象當如數組,但是老版本ie需要使用.get()        core_push.apply( ret, elems.get() );      }      return this.pushStack( ret );    };  });

  瀏覽器原生的插入節點的方法有兩個:appendChild和inserBefore,jQuery利用這兩個方法拓展了如下方法

  jQuery.fn.append使用this.appendChild( elem )

  jQuery.fn.prepend使用this.insertBefore( elem, this.firstChild )

  jQuery.fn.before使用this.parentNode.insertBefore( elem, this );

  jQuery.fn.after使用this.parentNode.insertBefore( elem, this.nextSibling );

  jQuery.fn.replaceWith 使用this.parentNode.insertBefore( elem, this.nextSibling);

  看一個例子的源碼(jQuery.fn.append)

  append: function() {      return this.domManip(arguments, true, function( elem ) {        if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {          this.appendChild( elem );        }      });    }

  根據上面的源碼。猜測domManip的作用是遍歷當前jQuery對象所匹配的元素,然后每個元素調用傳入的回調,并將要插入的節點(如果是字符串那么需要創建文檔碎片節點)作為傳入的回調的參數;并執行傳入的回調。

  接下來分析domManip,看猜測是否正確。dom即Dom元素,Manip是Manipulate的縮寫,連在一起的字面意思就是就是Dom操作。

a. domManip: function( args, table, callback )解析

  args 待插入的DOM元素或HTML代碼

  table 是否需要修正tbody,這個變量是優化的結果

  callback 回調函數,執行格式為callback.call( 目標元素即上下文, 待插入文檔碎片/單個DOM元素 )

  先看流程,再看細節

  第一步,變量初始化。其中iNoClone在后面會用到,如果當前的jQuery對象所匹配的元素不止一個(n > 1)的話,意味著構建出來的文檔碎片需要被n用到,則需要被克隆(n-1)次,加上碎片文檔本身才夠n次使用;value 是第一個參數args的第一個元素,后面會對value是函數做特殊處理;

var first, node, hasScripts,  scripts, doc, fragment,  i = 0,  l = this.length,  set = this,  iNoClone = l - 1,  value = args[0],  isFunction = jQuery.isFunction( value );

  第二步,處理特殊下要將當前jQuery對象所匹配的元素一一調用domManip。這種特殊情況有兩種:第一種,如果傳入的節點是函數(即value是函數)則需要當前jQuery對象所匹配的每個元素都將函數計算出的值作為節點代入domManip中處理。第二種,webkit下,我們不能克隆文含有checked的文檔碎片;克隆的文檔不能重復使用,那么只能是當前jQuery對象所匹配的每個元素都調用一次domManip處理。

//webkit下,我們不能克隆文含有checked的檔碎片if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {  return this.each(function( index ) {    var self = set.eq( index );    //如果args[0]是函數,則執行函數返回結果替換原來的args[0]    if ( isFunction ) {      args[0] = value.call( this, index, table ? self.html() : undefined );    }    self.domManip( args, table, callback );  });}

  第三步,處理正常情況,使用傳入的節點構建文檔碎片,并插入文檔中。這里面構建的文檔碎片就需要重復使用,區別于第二步的處理。這里面需要注意的是如果是script節點需要在加載完成后執行。順著源碼順序看一下過程

  構建文檔碎片

fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );first = fragment.firstChild;if ( fragment.childNodes.length === 1 ) {  fragment = first;}

  分離出其中的script,這其中有一個函數disableScript更改了script標簽的type值以確保安全,原來的type值是"text/javascript",改成了"true/text/javascript"或"false/text/javascript"

scripts = jQuery.map( getAll( fragment, "script" ), disableScript );hasScripts = scripts.length;

  文檔碎片插入頁面

for ( ; i < l; i++ ) {  node = fragment;  if ( i !== iNoClone ) {    node = jQuery.clone( node, true, true );    // Keep references to cloned scripts for later restoration    if ( hasScripts ) {      jQuery.merge( scripts, getAll( node, "script" ) );    }  }  callback.call(    table && jQuery.nodeName( this[i], "table" ) ?    findOrAppend( this[i], "tbody" ) :    this[i],    node,    i    );}

  執行script,分兩種情況,遠程的使用ajax來處理,本地的直接執行。

if ( hasScripts ) {  doc = scripts[ scripts.length - 1 ].ownerDocument;  // Reenable scripts  jQuery.map( scripts, restoreScript );  //在第一個文檔插入使執行可執行腳本  for ( i = 0; i < hasScripts; i++ ) {    node = scripts[ i ];    if ( rscriptType.test( node.type || "" ) &&      !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {      if ( node.src ) {        // Hope ajax is available...        jQuery.ajax({          url: node.src,          type: "GET",          dataType: "script",          async: false,          global: false,          "throws": true        });      } else {        jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );      }    }  }}

b. dom操作拓展

jQuery.fn.text

jQuery.fn.text: function( value ) {  return jQuery.access( this, function( value ) {    return value === undefined ?    jQuery.text( this ) :    this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );  }, null, value, arguments.length );}

   最終執行value === undefined ? jQuery.text( this ) : this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );

  其中jQuery.text = Sizzle.getText;

jQuery.fn.html

  函數使用jQuery.access來處理

  jQuery.fn.html: function( value ) {      return jQuery.access( this, function( value ) {...}, null, value, arguments.length );    }

  如果沒有參數表示是取值

if ( value === undefined ) {  return elem.nodeType === 1 ?  elem.innerHTML.replace( rinlinejQuery, "" ) :  undefined;}

  否則看是否能用innerHTML添加內容。點擊參考兼容問題

//看看我們是否可以走了一條捷徑,只需使用的innerHTML//需要執行的代碼script|style|link等不能使用innerHTML//htmlSerialize:確保link節點能使用innerHTML正確序列化,這就需要在IE瀏覽器的包裝元素//leadingWhitespace:IE strips使用.innerHTML需要以空白開頭//不是需要額外添加結束標簽或外圍包裝標簽的元素if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&  ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&  ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&  !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {  value = value.replace( rxhtmlTag, "<$1></$2>" );  try {    for (; i < l; i++ ) {        //移除元素節點和緩存,阻止內存泄漏        elem = this[i] || {};        if ( elem.nodeType === 1 ) {          jQuery.cleanData( getAll( elem, false ) );          elem.innerHTML = value;        }      }      elem = 0;    //如果使用innerHTML拋出異常,使用備用方法  } catch(e) {}}

  如果不能使用innerHTML或使用不成功(拋出異常),則使用備用方法append

//備用方法,使用append添加節點if ( elem ) {  this.empty().append( value );}  jQuery.fn.wrapAll(用單個標簽將所有匹配元素包裹起來)   處理步驟:  傳入參數是函數則將函數結果傳入if ( jQuery.isFunction( html ) ) {  return this.each(function(i) {    jQuery(this).wrapAll( html.call(this, i) );  });}  創建包裹層//獲得包裹標簽 The elements to wrap the target aroundvar wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);if ( this[0].parentNode ) {  wrap.insertBefore( this[0] );}

  用包裹裹住當前jQuery對象

wrap.map(function() {  var elem = this;  while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {    elem = elem.firstChild;  }   return elem;}).append( this );

  注意:當前jQuery對象匹配的元素最好只有一個,如果有多個的話不推薦使用,這種情況慎用,后面舉例可以看到。

  簡單的例子,原DOM為(后面都使用這個例子)

<div id='center' class="center">  <div id='ss' class="center">    <input type='submit' id='left' class="left">  </div></div><div class="right">我是right</div>  $('#center').wrapAll("<p></p>")后,dom變成了<p>  <div id="center" class="center">    <div id="ss" class="center">      <input type="submit" id="left" class="left">    </div>  </div></p><div class="right">我是right</div>

  慎用:如果當前jQuery所匹配的元素不止一個,例如原DOM執行$('div').wrapAll(“<p></p>”)后結果DOM變成

<p>  <div id="center" class="center"></div>  <div id="ss" class="center">    <input type="submit" id="left" class="left">  </div>  <div class="right">我是right</div></p>

  看到結果了吧,本來#center是#ss的父節點,結果變成了#ss的兄弟節點。

jQuery.fn.wrapInner(在每個匹配元素的所有子節點外部包裹指定的HTML結構)

  處理步驟:

  傳入參數是函數則將函數結果傳入

if ( jQuery.isFunction( html ) ) {  return this.each(function(i) {    jQuery(this).wrapInner( html.call(this, i) );  });}

  遍歷jQuery對象數組,獲取每個元素包含的內容(所有子節點)contents,然后使用warpAll包裹住contents

return this.each(function() {  var self = jQuery( this ),  contents = self.contents();  if ( contents.length ) {    contents.wrapAll( html );  } else {    self.append( html );  }});

  還是使用上面的例子中的原DOM,執行$('div').wrapInner('<p></p>')后結果DOM變成

<div id="center" class="center">  <p>    <div id="ss" class="center">      <p>        <input type="submit" id="left" class="left">      </p>    </div>  </p></div><div class="right">  <p>    我是right  </p></div>

jQuery.fn.wrap(在每個匹配元素外部包裹指定的HTML結構)

  對jQuery的每個元素分別使用wrapAll包裹一下

return this.each(function(i) {  jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );});

行$('div').wrap('<p></p>')后結果DOM變成

<p>  <div id="center" class="center">    <p>      <div id="ss" class="center">        <input type="submit" id="left" class="left">      </div>    </p>  </div></p><p>  <div class="right">我是right</div></p>

 jQuery.fn.unwrap(移除每個匹配元素的父元素)

  使用replaceWith用匹配元素父節點的所有子節點替換匹配元素的父節點。當然了父節點是body/html/document肯定是移除不了的

return this.parent().each(function() {  if ( !jQuery.nodeName( this, "body" ) ) {    jQuery( this ).replaceWith( this.childNodes );  }}).end();  執行$('div').wrap()后結果DOM變成<div id="ss" class="center">  <input type="submit" id="left" class="left"></div><div class="right">我是right</div>
  

jQuery.fn.remove(從文檔中移除匹配的元素)

  你還可以使用選擇器進一步縮小移除的范圍,只移除當前匹配元素中符合指定選擇器的部分元素。

  與detach()相比,remove()函數會同時移除與元素關聯綁定的附加數據( data()函數 )和事件處理器等(detach()會保留)。

for ( ; (elem = this[i]) != null; i++ ) {  if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) {    // detach傳入的參數keepData為true,不刪除緩存    if ( !keepData && elem.nodeType === 1 ) {      //清除緩存      jQuery.cleanData( getAll( elem ) );    }    if ( elem.parentNode ) {      if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {        setGlobalEval( getAll( elem, "script" ) );      }      elem.parentNode.removeChild( elem );    }  }}

  可以看到其中有一個重要的函數cleanData,該方法是用來清除緩存:遍歷每一個節點元素,對每一個節點元素做一下處理:

1.獲取當前元素對應的緩存

id = elem[ internalKey ];data = id && cache[ id ];

2.如果有綁定事件,則遍歷解綁事件

if ( data.events ) {  for ( type in data.events ) {    if ( special[ type ] ) {      jQuery.event.remove( elem, type );    //這是一個快捷方式,以避免jQuery.event.remove的開銷    } else {      jQuery.removeEvent( elem, type, data.handle );    }  }}

3.如果jQuery.event.remove沒有移除cache,則手動移除cache。其中IE需要做一些兼容處理,而且最終會將刪除歷史保存如core_deletedIds中

//當jQuery.event.remove沒有移除cache的時候,移除cacheif ( cache[ id ] ) {  delete cache[ id ];  //IE不允許從節點使用delete刪除expando特征,  //也能對文件節點使用removeAttribute函數;  //我們必須處理所有這些情況下,  if ( deleteExpando ) {    delete elem[ internalKey ];  } else if ( typeof elem.removeAttribute !== core_strundefined ) {    elem.removeAttribute( internalKey );  } else {    elem[ internalKey ] = null;  }  core_deletedIds.push( id );}

jQuery.fn.detach

detach: function( selector ) {      return this.remove( selector, true );    },

jQuery.fn.empty(清空每個匹配元素內的所有內容(所有子節點))

  函數將會移除每個匹配元素的所有子節點(包括文本節點、注釋節點等所有類型的節點),會清空相應的緩存數據。

for ( ; (elem = this[i]) != null; i++ ) {  //防止內存泄漏移除元素節點緩存  if ( elem.nodeType === 1 ) {    jQuery.cleanData( getAll( elem, false ) );  }  //移除所有子節點  while ( elem.firstChild ) {    elem.removeChild( elem.firstChild );  }  // IE<9,select節點需要將option置空  if ( elem.options && jQuery.nodeName( elem, "select" ) ) {    elem.options.length = 0;  }}
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品电影一区二区| 亚洲人成在线免费观看| 国内精品一区二区三区四区| 伊人伊成久久人综合网站| 亚洲国产黄色片| 国产精品稀缺呦系列在线| 成人午夜激情免费视频| 欧美日韩高清在线观看| 亚洲精品自在久久| 中文字幕日韩在线播放| 欧美日韩性生活视频| 成人国产在线激情| 精品高清一区二区三区| 欧美亚洲视频在线看网址| 国产日韩精品一区二区| 66m—66摸成人免费视频| 亚洲欧美一区二区三区在线| 国产精品揄拍一区二区| 久久久久久久久久久国产| 国产精品久久久久99| 久久精品青青大伊人av| 国产精品久久久久久久久借妻| 久久在线视频在线| 国产成人福利视频| 欧美天天综合色影久久精品| 成人在线播放av| 亚洲人精品午夜在线观看| 成人在线观看视频网站| 色综合久综合久久综合久鬼88| 欧美亚洲国产另类| 中文字幕在线国产精品| 午夜精品一区二区三区在线视频| 国产精品久久久久久久午夜| 精品一区电影国产| 欧美肥老妇视频| 亚洲精品日产aⅴ| 亚洲国产精品久久久久| 亚洲伊人久久综合| 欧美电影在线播放| 啊v视频在线一区二区三区| 狠狠色狠色综合曰曰| 日本精品视频在线观看| 国内精品400部情侣激情| 日韩免费观看网站| 久久伊人精品视频| 中日韩美女免费视频网址在线观看| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美性高潮床叫视频| 亚洲一区二区三区xxx视频| 成人性生交大片免费看小说| 中文字幕不卡在线视频极品| 亚洲一区二区三区视频播放| 国产综合在线观看视频| 国产精品69久久久久| 久操成人在线视频| 欧美精品激情blacked18| 亚洲欧美在线一区| 色在人av网站天堂精品| 欧美激情成人在线视频| 国产偷亚洲偷欧美偷精品| 欧美黑人性视频| 国产女人18毛片水18精品| 欧美亚洲国产日韩2020| 久久久国产在线视频| 亚洲欧美制服综合另类| 国产免费一区二区三区在线能观看| 色在人av网站天堂精品| 日韩中文字幕久久| 色综合影院在线| 亚洲欧美成人在线| 国产成人精品免高潮在线观看| 国产精品久久999| 亚洲黄页网在线观看| 日韩av免费在线| 亚洲人av在线影院| 日本a级片电影一区二区| 日韩视频免费在线| 久久久久久亚洲精品| 国产精品视频久久久久| 欧美亚洲在线播放| 久久久久久噜噜噜久久久精品| 欧美黄色片免费观看| 亚洲欧洲第一视频| 91国产中文字幕| 日韩中文理论片| 中文字幕精品在线视频| 亚洲天堂av在线免费观看| 国内精品在线一区| 91在线免费视频| 97精品伊人久久久大香线蕉| 国产经典一区二区| 国产午夜精品免费一区二区三区| 国产精品视频区1| 欧美精品在线观看91| 国产午夜精品一区理论片飘花| 日本精品一区二区三区在线播放视频| 精品一区二区电影| 国产成人精品久久亚洲高清不卡| 欧美激情视频网| 亚洲女人被黑人巨大进入al| 在线观看日韩www视频免费| 国产女人精品视频| 色综合天天狠天天透天天伊人| 18性欧美xxxⅹ性满足| 国产成人精品一区二区三区| 国产亚洲人成网站在线观看| 欧美日韩免费区域视频在线观看| 成人动漫网站在线观看| 国产成人精品在线| 中文国产成人精品| 欧美电影电视剧在线观看| 精品国产欧美一区二区五十路| 国产裸体写真av一区二区| 欧美丰满少妇xxxxx做受| 亚洲在线免费视频| 欧美电影免费观看网站| 日韩精品免费观看| 92版电视剧仙鹤神针在线观看| 中文字幕久久亚洲| 91天堂在线观看| 国产精品美女av| 亚洲国产高清高潮精品美女| 91精品国产高清久久久久久| 国产自产女人91一区在线观看| 欧美日韩免费观看中文| 亚洲精品videossex少妇| 日韩视频精品在线| 精品久久久香蕉免费精品视频| 亚洲午夜av电影| 91极品视频在线| 日韩福利伦理影院免费| 国产精品日日摸夜夜添夜夜av| 97久久精品国产| 成人网中文字幕| 欧美肥臀大乳一区二区免费视频| 国产美女扒开尿口久久久| 永久免费精品影视网站| 亚洲自拍小视频免费观看| 国产亚洲欧美日韩一区二区| 97人人爽人人喊人人模波多| 欧美激情精品久久久久久| 国产精品视频色| 国产成人精品久久亚洲高清不卡| 亚洲欧美日韩图片| 欧美大片大片在线播放| 日韩精品视频免费| 欧美在线中文字幕| 精品中文字幕乱| 91亚洲精品久久久久久久久久久久| 91精品视频免费观看| 精品国内产的精品视频在线观看| 日韩精品中文字幕久久臀| 国产精品美女无圣光视频| 日韩视频免费在线| 欧美高清在线播放| 九九精品在线观看| 91理论片午午论夜理片久久| 欧美精品video| 欧美激情三级免费| 久久综合伊人77777尤物| 欧美性受xxxx黑人猛交| 尤物精品国产第一福利三区| 日韩精品高清视频| 亚洲欧洲日韩国产|