jQuery.clean使用方法及思路分析


Posted in Javascript onJanuary 07, 2013

一、jQuery.clean使用方法
jQuery.clean( elems, context, fragment, scripts );
二、思路分析
1、处理参数context,确保其为文档根节点document
2、处理参数elems数组(循环遍历数组)

2.1、elem为数字,转换为字符串

2.2、elem为非法值,跳出本次循环

2.3、elem为字符串

2.4、字符串不存在实体编号或html标签,则创建文本节点

2.5、字符串为实体编号或html标签

创建一个div元素并插入到文档碎片中
 处理xhtml风格标签
 将elem包裹起来,并将包裹后的字符串作为div的innerHTML
 如果包裹深度大于1,只留下第一层包裹元素
 清除在ie6,7中空table标签自动加入的tbody
 将在ie9以下浏览器中剔除的开头空白字符串作为div元素的第一个文本子节点
 将elem重新赋值为div的子节点集合(nodeList对象),
 移除本次循环中文档碎片中的div,保持下一次循环中干净的div元素

2.3、如果elem为文本节点,则直接添加到要返回的ret数组中,否则将elem(nodeList对象)中的节点合并到数组

2.4、修复在ie6、7中type为radio,checkbox类型的节点的选中状态(checked)失效的bug
3、处理参数fragment

3.1、将ret中各节点添加到文档碎片fragment中

3.2、提取节点中的script子节点,并将其添加到ret数组中,添加的script位置为其原父元素位置后面
4、返回ret数组
三、源码注释分析
1、函数中用到的变量及函数

var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
         "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
     wrapMap = {
         option: [ 1, "<select multiple='multiple'>", "</select>" ],
         legend: [ 1, "<fieldset>", "</fieldset>" ],
         thead: [ 1, "<table>", "</table>" ],
         tr: [ 2, "<table><tbody>", "</tbody></table>" ],
         td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
         col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
         area: [ 1, "<map>", "</map>" ],
         _default: [ 0, "", "" ]
     },
     rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
     rtagName = /<([\w:]+)/,
     rtbody = /<tbody/i,
     rhtml = /<|&#?\w+;/,
     rleadingWhitespace = /^\s+/,
     rcheckableType = /^(?:checkbox|radio)$/,
     rscriptType = /\/(java|ecma)script/i;
 // 设置复选框checkbox或单选框radio表单元素的默认选中状态
 function fixDefaultChecked( elem ) {
     if ( rcheckableType.test( elem.type ) ) {
         elem.defaultChecked = elem.checked;
     }
 }
 // 创建一个安全的文档碎片
 function createSafeFragment( document ) {
     var list = nodeNames.split( "|" ),
     safeFrag = document.createDocumentFragment(); // ie6,7,8浏览器把safeFrage作为HTMLDocument类型
     // 针对ie9以下浏览器
     if ( safeFrag.createElement ) {
         while ( list.length ) {
             safeFrag.createElement(
                 list.pop()
             );
         }
     }
     return safeFrag;
 }
 // 模拟ES5中Array的新功能
 // 该函数API:http://www.css88.com/jqapi-1.8/#p=jQuery.grep
 jQuery.extend({
     grep: function( elems, callback, inv ) {
         var retVal,
             ret = [],
             i = 0,
             length = elems.length;
         inv = !!inv;
         // Go through the array, only saving the items
         // that pass the validator function
         for ( ; i < length; i++ ) {
             retVal = !!callback( elems[ i ], i );
             if ( inv !== retVal ) {
                 ret.push( elems[ i ] );
             }
         }
         return ret;
     }              
 });

2、源码分析
jQuery.extend({
     clean: function( elems, context, fragment, scripts ) {
         // 声明变量
         var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags,
             safe = context === document && safeFragment,
             ret = [];
         // 确保变量context为文档根节点document
         if ( !context || typeof context.createDocumentFragment === "undefined" ) {
             context = document;
         }
         // Use the already-created safe fragment if context permits
         for ( i = 0; (elem = elems[i]) != null; i++ ) {
             // 如果elem为数字,则将其转换为字符串
             if ( typeof elem === "number" ) {
                 elem += "";
             }
             // 如果elem为undefined,跳出本次循环
             if ( !elem ) {
                 continue;
             }
             // Convert html string into DOM nodes
             // 转换数组项(字符串)为DOM节点
             if ( typeof elem === "string" ) {
                 // 如果不存在html实体编号或标签,则创建文本节点
                 if ( !rhtml.test( elem ) ) {
                     elem = context.createTextNode( elem );
                 }
                 // 处理是html标签字符串的数组项
                 else {
                     // Ensure a safe container in which to render the html
                     // safe为#document-fragment类型,在ie9以下浏览器中,safe为HTMLDocument类型节点,且nodeNames数组为空
                     safe = safe || createSafeFragment( context );
                     // 创建一个div元素并将其插入到文档碎片中
                     div = context.createElement("div");
                     safe.appendChild( div );
                     // Fix "XHTML"-style tags in all browsers
                     // 除了area,br,col,embed,hr,img,input,link,meta,param这些标签外,
                     // 将开始标签末尾加入斜杠的标签转换为开始和结束标签
                     elem = elem.replace(rxhtmlTag, "<$1></$2>");
                     // Go to html and back, then peel off extra wrappers
                     // 获取左边第一个标签元素
                     tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
                     // 获取最外层元素的包裹元素,并将元素包裹在其中
                     wrap = wrapMap[ tag ] || wrapMap._default;
                     depth = wrap[0];
                     div.innerHTML = wrap[1] + elem + wrap[2];
                     // Move to the right depth
                     // 如果元素的包裹深度大于1,div重新赋值为元素最近的包裹元素(即:包含第一层包裹元素)
                     while ( depth-- ) {
                         div = div.lastChild;
                     }
                     // Remove IE's autoinserted <tbody> from table fragments
                     // 在IE6,7中,清除字符串中空table标签中自动加入的tbody标签(手动加入的除外)
                     if ( !jQuery.support.tbody ) {
                         // String was a <table>, *may* have spurious(伪造的) <tbody>
                         // 判断字符串中是否拥有空tbody标签
                         hasBody = rtbody.test(elem);
                         // 如果最外层标签为table且table中没有手动加入tbody
                         // 变量tbody为div.firstChild.childNodes(自动加入的tbody标签集合)
                         tbody = tag === "table" && !hasBody ?
                             div.firstChild && div.firstChild.childNodes :
                             // String was a bare <thead> or <tfoot>
                             // 如果字符串中仅有一个空thead或tfoot标签
                             // 变量tbody为div.childNodes(字符串中的thead和tfoot标签集合)
                             wrap[1] === "<table>" && !hasBody ?
                                 div.childNodes :
                                 [];
                         for ( j = tbody.length - 1; j >= 0 ; --j ) {
                             // 排除thead或tfoot标签
                             if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
                                 // 清除空table标签中自动加入的tbody
                                 tbody[ j ].parentNode.removeChild( tbody[ j ] );
                             }
                         }
                     }
                     // IE completely kills leading whitespace when innerHTML is used
                     // 在ie9以下浏览器中,字符串以空白字符串开头,将空白字符串作为div元素的第一个文本子节点
                     if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
                         div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
                     }
                     // 获取已经处理完毕的div子节点集合(nodeList对象)
                     elem = div.childNodes;
                     // Take out of fragment container (we need a fresh div each time)
                     // 在下一次循环处理字符串数组项前,清除处理创建过的div元素
                     div.parentNode.removeChild( div );
                 }
             }
             // 如果elem为DOM节点(文本节点)
             if ( elem.nodeType ) {
                 ret.push( elem );
             }
             // 将nodeList对象中节点合并到返回的数组中
             else {
                 jQuery.merge( ret, elem );
             }
         }
         // Fix #11356: Clear elements from safeFragment
         if ( div ) {
             elem = div = safe = null;
         }
         // Reset defaultChecked for any radios and checkboxes
         // about to be appended to the DOM in IE 6/7 (#8060)
         // 在ie6,7中,拥有checked属性的单选按钮,复选框在插入到其他标签后,选中状态会失效(下面代码修复该bug)
         if ( !jQuery.support.appendChecked ) {
             for ( i = 0; (elem = ret[i]) != null; i++ ) {
                 if ( jQuery.nodeName( elem, "input" ) ) {
                     fixDefaultChecked( elem );
                 } else if ( typeof elem.getElementsByTagName !== "undefined" ) {
                     jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
                 }
             }
         }
         // Append elements to a provided document fragment
         // 将ret数组中的各DOM节点插入到提供的文档碎片中
         // 提取dom节点中的script节点,并添加到ret数组中,位置为其原父元素索引位置后
         if ( fragment ) {
             // Special handling of each script element
             handleScript = function( elem ) {
                 // Check if we consider it executable
                 // 如果elem元素不存在type属性或者type值为javascript或者为ecmascript
                 if ( !elem.type || rscriptType.test( elem.type ) ) {
                     // Detach the script and store it in the scripts array (if provided) or the fragment
                     // Return truthy to indicate that it has been handled
                     return scripts ?
                         scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
                         fragment.appendChild( elem );
                 }
             };
             for ( i = 0; (elem = ret[i]) != null; i++ ) {
                 // Check if we're done after handling an executable script
                 if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
                     // Append to fragment and handle embedded scripts
                     // 将elem元素添加到文档碎片中并处理嵌入的脚本(script标签元素)
                     fragment.appendChild( elem );
                     if ( typeof elem.getElementsByTagName !== "undefined" ) {
                         // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
                         jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
                         // Splice the scripts into ret after their former ancestor and advance our index beyond them
                         // 将script标签添加到数组,位置为其原父元素索引位置后
                         ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
                         i += jsTags.length;
                     }
                 }
             }
         }
         return ret;
     }
 });
Javascript 相关文章推荐
jQuery chili图片远处放大插件
Nov 30 Javascript
JavaScript高级程序设计(第3版)学习笔记 概述
Oct 11 Javascript
jQuery使用hide方法隐藏指定元素class样式用法实例
Mar 30 Javascript
node.js cookie-parser 中间件介绍
Jun 06 Javascript
JS模拟的Map类实现方法
Jun 17 Javascript
Bootstrap源码解读按钮(5)
Dec 23 Javascript
vue之数据交互实例代码
Jun 16 Javascript
React组件重构之嵌套+继承及高阶组件详解
Jul 19 Javascript
Javascript读写cookie的实例源码
Mar 16 Javascript
vue 使用鼠标滚动加载数据的例子
Oct 31 Javascript
高效jQuery选择器的5个技巧实例分析
Nov 26 jQuery
详解node和ES6的模块导出与导入
Feb 19 Javascript
js获取height和width的方法说明
Jan 06 #Javascript
javascript时间自动刷新实现原理与步骤
Jan 06 #Javascript
javaScript让文本框内的最后一个文字的后面获得焦点实现代码
Jan 06 #Javascript
一些常用弹出窗口/拖放/异步文件上传等实用代码
Jan 06 #Javascript
javascript编码的几个方法详细介绍
Jan 06 #Javascript
javascript返回顶部效果(自写代码)
Jan 06 #Javascript
让你的博客飘雪花超出屏幕依然看得见
Jan 04 #Javascript
You might like
php zip文件解压类代码
2009/12/02 PHP
微信公众平台网页授权获取用户基本信息中授权回调域名设置的变动
2014/10/21 PHP
PHP实现动态添加XML中数据的方法
2018/03/30 PHP
Gambit vs CL BO3 第三场 2.13
2021/03/10 DOTA
Exitjs获取DataView中图片文件名
2009/11/26 Javascript
鼠标滑在标题上显示图片的JS代码
2013/11/19 Javascript
jquery动态加载select下拉框示例代码
2013/12/10 Javascript
JQuery动画animate的stop方法使用详解
2014/05/09 Javascript
Javascript实现简单二级下拉菜单实例
2014/06/15 Javascript
$(&quot;&quot;).click与onclick的区别示例介绍
2014/09/25 Javascript
jquery手风琴特效插件
2015/02/04 Javascript
JavaScript DOM基础
2015/04/13 Javascript
基于jQuery实现仿百度首页换肤背景图片切换代码
2015/08/25 Javascript
微信小程序 倒计时组件实现代码
2016/10/24 Javascript
Bootstrap基本插件学习笔记之按钮(21)
2016/12/08 Javascript
详解jQuery选择器
2016/12/21 Javascript
JavaScript实现移动端轮播效果
2017/06/06 Javascript
使用AngularJS对表单提交内容进行验证的操作方法
2017/07/12 Javascript
JS获取月的第几周和年的第几周实例代码
2018/12/05 Javascript
Vue 实现监听窗口关闭事件,并在窗口关闭前发送请求
2020/09/01 Javascript
[02:51]DOTA2英雄基础教程 风暴之灵
2013/12/23 DOTA
对python模块中多个类的用法详解
2019/01/10 Python
分享30个新鲜的CSS3打造的精美绚丽效果(附演示下载)
2012/12/28 HTML / CSS
Elizabeth Gage官网:英国最好的珠宝设计之一
2020/09/26 全球购物
部队领导证婚词
2014/01/12 职场文书
会计学专业学生的求职信范文
2014/01/27 职场文书
廉洁教育学习材料
2014/05/19 职场文书
理想演讲稿范文
2014/05/21 职场文书
中职生自荐信范文
2014/06/15 职场文书
公民授权委托书
2014/10/15 职场文书
中班教师个人总结
2015/02/05 职场文书
2015年健康教育工作总结
2015/04/10 职场文书
民事纠纷协议书
2016/03/23 职场文书
Golang 使用Map实现去重与set的功能操作
2021/04/29 Golang
SpringBoot+VUE实现数据表格的实战
2021/08/02 Java/Android
Python可视化神器pyecharts之绘制箱形图
2022/07/07 Python