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 相关文章推荐
js实现按钮控制图片360度翻转特效的方法
Feb 17 Javascript
JavaScript函数使用的基本教程
Jun 04 Javascript
JavaScript中几种排序算法的简单实现
Jul 29 Javascript
JavaScript判断变量是否为数组的方法(Array)
Feb 24 Javascript
超漂亮的Bootstrap 富文本编辑器summernote
Apr 05 Javascript
jQuery检查元素存在性(推荐)
Sep 17 Javascript
js实现九宫格拼图小游戏
Feb 13 Javascript
vue实现动态添加数据滚动条自动滚动到底部的示例代码
Jul 06 Javascript
如何制作一个Node命令行图像识别工具
Dec 12 Javascript
javascript将16进制的字符串转换为10进制整数hex
Mar 05 Javascript
vue中的过滤器及其时间格式化问题
Apr 09 Javascript
浅谈Vue开发人员的7个最好的VSCode扩展
Jan 20 Vue.js
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 和 MySQL 基础教程(一)
2006/10/09 PHP
彻底杜绝PHP的session cookie错误
2009/08/09 PHP
php实现PDO中捕获SQL语句错误的方法
2017/02/16 PHP
php str_getcsv把字符串解析为数组的实现方法
2017/04/05 PHP
实例分析PHP中PHPMailer发邮件
2017/12/13 PHP
如何在Mozilla Gecko 用Javascript加载XSL
2007/01/09 Javascript
JavaScript入门教程(12) js对象化编程
2009/01/31 Javascript
JS数组(Array)处理函数整理
2014/12/07 Javascript
JS实现状态栏跑马灯文字效果代码
2015/10/24 Javascript
JS+JSP通过img标签调用实现静态页面访问次数统计的方法
2015/12/14 Javascript
WordPress中鼠标悬停显示和隐藏评论及引用按钮的实现
2016/01/12 Javascript
Javascript实现图片加载从模糊到清晰显示的方法
2016/06/21 Javascript
EasyUI创建对话框的两种方式
2016/08/23 Javascript
jquery 动态合并单元格的实现方法
2016/08/26 Javascript
AngularJS动态加载模块和依赖的方法分析
2016/11/08 Javascript
Angular使用 ng-img-max 调整浏览器中的图片的示例代码
2017/08/17 Javascript
使用JS实现图片轮播的实例(前后首尾相接)
2017/09/21 Javascript
在vue项目中安装使用Mint-UI的方法
2017/12/27 Javascript
微信小程序登录对接Django后端实现JWT方式验证登录详解
2019/07/29 Javascript
layui table 列宽百分比显示的实现方法
2019/09/28 Javascript
vue动态渲染svg、添加点击事件的实现
2020/03/13 Javascript
Python抓取Discuz!用户名脚本代码
2013/12/30 Python
Python程序员鲜为人知但你应该知道的17个问题
2014/06/04 Python
python根据时间生成mongodb的ObjectId的方法
2015/03/13 Python
python遍历类中所有成员的方法
2015/03/18 Python
Django模板变量如何传递给外部js调用的方法小结
2017/07/24 Python
python opencv 读取本地视频文件 修改ffmpeg的方法
2019/01/26 Python
Python变量类型知识点总结
2019/02/18 Python
PyQt5 实现给窗口设置背景图片的方法
2019/06/13 Python
将python依赖包打包成window下可执行文件bat方式
2019/12/26 Python
pytorch实现保证每次运行使用的随机数都相同
2020/02/20 Python
工作保证书范文
2014/04/29 职场文书
党的群众路线教育实践活动对照检查材料范文
2014/09/24 职场文书
瘦西湖导游词
2015/02/03 职场文书
SpringBoot生成License的实现示例
2021/06/16 Java/Android
Python自动化工具之实现Excel转Markdown表格
2022/04/08 Python