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实现的可隐藏重现的靠边悬浮层实例代码
May 27 Javascript
jQuery 1.9移除了$.browser可以使用$.support来替代
Sep 03 Javascript
JavaScript中constructor()方法的使用简介
Jun 05 Javascript
jQuery控制div实现随滚动条滚动效果
Jun 07 Javascript
jQuery.Form上传文件操作
Feb 05 Javascript
bootstrap+jQuery 实现下拉菜单中复选框全选和全不选效果
Jun 12 jQuery
在Vue中如何使用Cookie操作实例
Jul 27 Javascript
ionic选择多张图片上传的示例代码
Oct 10 Javascript
详解微信小程序canvas圆角矩形的绘制的方法
Aug 22 Javascript
vue2.0 使用element-ui里的upload组件实现图片预览效果方法
Sep 04 Javascript
详解Vue的常用指令v-if, v-for, v-show,v-else, v-bind, v-on
Oct 12 Javascript
vue动态设置页面title的方法实例
Aug 23 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
解决File size limit exceeded 错误的方法
2013/06/14 PHP
php数组转换js数组操作及json_encode的用法详解
2013/10/26 PHP
PHP面向对象继承用法详解(优化与减少代码重复)
2016/12/02 PHP
PHP环形链表实现方法示例
2017/09/15 PHP
Laravel 模型使用软删除-左连接查询-表起别名示例
2019/10/24 PHP
使用Json比用string返回数据更友好,也更面向对象一些
2011/09/13 Javascript
jQuery 鼠标经过(hover)事件的延时处理示例
2014/04/14 Javascript
javascript实现锁定网页、密码解锁效果(类似系统屏幕保护效果)
2014/08/15 Javascript
jQuery编程中的一些核心方法简介
2015/08/14 Javascript
jQuery右侧选项卡焦点图片轮播特效代码分享
2015/09/05 Javascript
JS实现复制内容到剪贴板功能兼容所有浏览器(推荐)
2016/06/17 Javascript
Bootstrap基本组件学习笔记之分页(12)
2016/12/08 Javascript
arcgis for js栅格图层叠加(Raster Layer)问题
2017/11/22 Javascript
vue.js动画中的js钩子函数的实现
2018/07/06 Javascript
解决Vue axios post请求,后台获取不到数据的问题方法
2018/08/11 Javascript
微信小程序自定义toast的实现代码
2018/11/16 Javascript
35个最好用的Vue开源库(史上最全)
2019/01/03 Javascript
JQuery常见节点操作实例分析
2019/05/15 jQuery
layui使用form表单实现post请求页面跳转的方法
2019/09/14 Javascript
如何实现iframe父子传参通信
2020/02/05 Javascript
python实现类似ftp传输文件的网络程序示例
2014/04/08 Python
零基础写python爬虫之打包生成exe文件
2014/11/06 Python
在win和Linux系统中python命令行运行的不同
2016/07/03 Python
Python实现PS滤镜Fish lens图像扭曲效果示例
2018/01/29 Python
讲解Python3中NumPy数组寻找特定元素下标的两种方法
2019/08/04 Python
xadmin使用formfield_for_dbfield函数过滤下拉表单实例
2020/04/07 Python
Python函数必须先定义,后调用说明(函数调用函数例外)
2020/06/02 Python
路易威登和香奈儿手袋:LuxeDH
2017/01/12 全球购物
N:Philanthropy官网:美国洛杉矶基础款服装
2020/06/09 全球购物
财务助理岗位职责
2013/11/10 职场文书
食品厂厂长岗位职责
2014/01/30 职场文书
2014年员工工作总结范文
2014/11/18 职场文书
关于清明节的演讲稿2015
2015/03/18 职场文书
2015年五一劳动节演讲稿
2015/03/18 职场文书
八月一日观后感
2015/06/10 职场文书