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代码
May 28 Javascript
Google Dart编程语法和基本类型学习教程
Nov 27 Javascript
JavaScript精炼之构造函数 Constructor及Constructor属性详解
Nov 05 Javascript
基于jQuery和CSS3制作响应式水平时间轴附源码下载
Dec 20 Javascript
jQuery实现点击查看大图并以弹框的形式居中
Aug 08 Javascript
利用jQuery插件imgAreaSelect实现图片上传裁剪(放大缩小)
Dec 02 Javascript
jQuery实现Table表格隔行变色及高亮显示当前选择行效果示例
Feb 14 Javascript
BootStrap Fileinput上传插件使用实例代码
Jul 28 Javascript
node.js自动上传ftp的脚本分享
Jun 16 Javascript
Vue-router 中hash模式和history模式的区别
Jul 24 Javascript
jQuery设置下拉框显示与隐藏效果的方法分析
Sep 15 jQuery
jquery实现烟花效果(面向对象)
Mar 10 jQuery
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输出含有“#”字符串的方法
2017/01/18 PHP
PHP获取本周所有日期或者最近七天所有日期的方法
2018/06/20 PHP
一个简单的JavaScript 日期计算算法
2009/09/11 Javascript
基于JQuery的cookie插件
2010/04/07 Javascript
jquery实现marquee效果(文字或者图片的水平垂直滚动)
2013/01/07 Javascript
js实现右下角提示框的方法
2015/02/03 Javascript
JQuery select(下拉框)操作方法汇总
2015/04/15 Javascript
jQuery仿gmail实现fixed布局的方法
2015/05/27 Javascript
jQuery树形下拉菜单特效代码分享
2015/08/15 Javascript
Sortable.js拖拽排序使用方法解析
2016/11/04 Javascript
Extjs让combobox写起来简洁又漂亮
2017/01/05 Javascript
node.js实现登录注册页面
2017/04/08 Javascript
在layui tab控件中载入外部html页面的方法
2019/09/04 Javascript
Vue中this.$nextTick的作用及用法
2020/02/04 Javascript
Python中map和列表推导效率比较实例分析
2015/06/17 Python
python读取和保存视频文件
2018/04/16 Python
PyQt5固定窗口大小的方法
2019/06/18 Python
python中关于数据类型的学习笔记
2020/07/19 Python
Ann Taylor官方网站:美国最大的女性产品制造商之一
2016/09/14 全球购物
联想西班牙官网:Lenovo西班牙
2018/08/28 全球购物
Sunglasses Shop英国:欧洲领先的太阳镜在线供应商之一
2018/09/19 全球购物
女性时尚网购:Chic Me
2019/07/30 全球购物
Paradox London官方网站:英国新娘鞋婚礼鞋品牌
2019/08/29 全球购物
师范应届毕业生自荐信
2013/11/18 职场文书
校园门卫岗位职责
2013/12/09 职场文书
优秀学生自我鉴定范例
2013/12/18 职场文书
消防安全员岗位职责
2014/03/10 职场文书
总经理助理的职责
2014/03/14 职场文书
员工工作表现评语
2014/04/26 职场文书
创先争优标语
2014/06/27 职场文书
2014四风问题对照检查材料范文
2014/09/15 职场文书
认错检讨书
2014/10/02 职场文书
民政局离婚协议书范本
2014/10/20 职场文书
建国大业观后感800字
2015/06/01 职场文书
pandas 操作 Excel操作总结
2021/03/31 Python
Vue.Draggable实现交换位置
2022/04/07 Vue.js