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中操控hidden、disable等无值属性的方法
Jan 06 Javascript
如何防止回车(enter)键提交表单
May 11 Javascript
深入探讨javascript函数式编程
Oct 11 Javascript
javascript实现动态统计图开发实例
Nov 21 Javascript
BootStrap数据表格实例代码
Sep 13 Javascript
JS实现多物体运动的方法详解
Jan 23 Javascript
超好用的jQuery分页插件jpaginate用法示例【附源码下载】
Dec 06 jQuery
微信公众号平台接口开发 获取微信服务器IP地址方法解析
Aug 14 Javascript
vue.js实现图书管理功能
Sep 24 Javascript
微信小程序关键字变色实现代码实例
Dec 13 Javascript
如何利用JS将手机号中间四位变成*号
Sep 29 Javascript
详解基于element的区间选择组件校验(交易金额)
Jan 07 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版)
2006/10/09 PHP
详解ThinkPHP3.2.3验证码显示、刷新、校验
2016/12/29 PHP
PHP实现权限管理功能示例
2017/09/22 PHP
两种WEB下的模态对话框 (asp.net或js的分别实现)
2009/12/02 Javascript
JavaScript中链式调用之研习
2011/04/07 Javascript
html dom节点操作(获取/修改/添加或删除)
2014/01/23 Javascript
JQ技术实现注册页面带有校验密码强度
2015/07/27 Javascript
JS+CSS实现类似QQ好友及黑名单效果的树型菜单
2015/09/22 Javascript
百度地图给map添加右键菜单(判断是否为marker)
2016/03/04 Javascript
深入理解关于javascript中apply()和call()方法的区别
2016/04/12 Javascript
vue.js整合vux中的上拉加载下拉刷新实例教程
2018/01/09 Javascript
vue.js多页面开发环境搭建过程
2019/04/24 Javascript
react结合bootstrap实现评论功能
2020/05/30 Javascript
python对数组进行反转的方法
2015/05/20 Python
python删除过期文件的方法
2015/05/29 Python
Python提取Linux内核源代码的目录结构实现方法
2016/06/24 Python
python实现字符串连接的三种方法及其效率、适用场景详解
2017/01/13 Python
Python 的类、继承和多态详解
2017/07/16 Python
Python基于贪心算法解决背包问题示例
2017/11/27 Python
python 巧用正则寻找字符串中的特定字符的位置方法
2018/05/02 Python
Python处理命令行参数模块optpars用法实例分析
2018/05/31 Python
用scikit-learn和pandas学习线性回归的方法
2019/06/21 Python
python实现把两个二维array叠加成三维array示例
2019/11/29 Python
python GUI库图形界面开发之PyQt5控件QTableWidget详细使用方法与属性
2020/02/25 Python
Python socket连接中的粘包、精确传输问题实例分析
2020/03/24 Python
Django实现内容缓存实例方法
2020/06/30 Python
Python绘制词云图之可视化神器pyecharts的方法
2021/02/23 Python
自我评价范文
2013/12/22 职场文书
高中生物教学反思
2014/02/05 职场文书
《沙漠中的绿洲》教学反思
2014/04/24 职场文书
群众路线教育实践活动学习笔记
2014/11/05 职场文书
2015年幼儿园新年寄语
2014/12/08 职场文书
辞职离别感言
2015/08/04 职场文书
python代码实现扫码关注公众号登录的实战
2021/11/01 Python
聊聊mysql都有哪几种分区方式
2022/04/13 MySQL
JS前端轻量fabric.js系列物体基类
2022/08/05 Javascript