jQuery源码阅读(七)--init()遗留部分buildFragment()函数

本文主要是介绍jQuery源码阅读(七)--init()遗留部分buildFragment()函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在 jQuery源码阅读(五)—init函数中,已经分析了init函数逻辑的大头,即参数selector为字符串的形式,但这里边仍然有两个为深入看的方法,一个是当selector是复杂标签的形式时,调用的bildFragment()方法,另一个是当selector为各种选择器时,调Sizzle模块的find()方法。

这一篇先来看buildFragment()函数的源码,分析该函数在处理参数为复杂标签时是如何做的?至于Sizzle模块的find()方法,打算后面阅读Sizzle选择器部分的源码时再来分析整理。

buildFragment()用法

要分析一个函数怎么实现的,首先得知道这个函数是干嘛的。所以,我们先来看看buildFragment函数是用来干什么的?

//调jQuery的静态方法
jQuery.buildFragment( ['<ul><li>苹果</li><li>柠檬</li></ul>',], [document] )

先来看看函数返回什么?
这里写图片描述
返回了一个对象,有两个属性,一个是为布尔值的cacheable,另一个是为文碎片的fragment。

{cacheable: true,fragment: ducoment-fragment
}

再打开fragment看看
这里写图片描述
可以看到,fragment里面childNodes有一个元素ul, 然后在ul 里面childNodes又有两个li元素,看到这,我们大概就知道buildFragment这个函数的作用了,他就是将我们传入的包含有标签的字符串,转换成对应的文档碎片,并记录下来。

那么到底在源码里面是如何实现的?下来我们一步步分析。

buildFragment()源码

就用我们上面说的jQuery.buildFragment( ['<ul><li>苹果</li><li>柠檬</li></ul>',], [document] )这个例子对照着源码走一遍。

jQuery.buildFragment = function( args, nodes, scripts ) {//这几个变量,先不管,后面用到再分析var fragment, cacheable, cacheresults, doc,//第一个参数(数组)的第一个元素(这里我们例子中是'<ul><li>苹果</li><li>柠檬</li></ul>)first = args[ 0 ];// nodes may contain either an explicit document object, a jQuery collection or context object.// If nodes[0] contains a valid object to assign to doc//这里类似于我们之前的context,一般都是documentif ( nodes && nodes[0] ) {doc = nodes[0].ownerDocument || nodes[0];}// Ensure that an attr object doesn't incorrectly stand in as a document object// Chrome and Firefox seem to allow this to occur and will throw exception// Fixes #8950if ( !doc.createDocumentFragment ) {doc = document;}// 判断当前标签是否可缓存,可缓存就将之前的变量cacheable设为true,这是一个比较重要的标志,后面详细分析如何判断标签是否是可缓存的if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&first.charAt(0) === "<" && !rnocache.test( first ) &&(jQuery.support.checkClone || !rchecked.test( first )) &&(jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {cacheable = true;//读取缓存中的该标签cacheresults = jQuery.fragments[ first ];if ( cacheresults && cacheresults !== 1 ) {fragment = cacheresults;}}if ( !fragment ) {  //fragment为空,有两种情况,一种是标签不可缓存,没有进入上一步的判读中;另一种是标签可缓存,但是还没有缓存,即第一次碰到这个标签//这种情况下,先创建一个文档片段,为了存放后面创建的DOM元素,接着去调jQuery.clean函数,这个后面再单独分析fragment = doc.createDocumentFragment();jQuery.clean( args, doc, fragment, scripts );}if ( cacheable ) {//如果可以缓存,存起来jQuery.fragments[ first ] = cacheresults ? fragment : 1;}//最后返回含有两个元素的对象,分别是代码片段和可否缓存标志return { fragment: fragment, cacheable: cacheable };
};

看完上面代码,可以看到主要的操作都是在jQuery.clean函数中进行的,下来我们看看jQuery.clean函数是如何做的?
还是先来看clean函数是干什么的?

jQuery.clean(['<ul><li>苹果</li><li>柠檬</li></ul>', '<div></div>'], [document])     //返回一个数组

这里写图片描述

可以看到,jQuery.clean函数将输入的含有标签的字符串转换成了DOM元素,并存在数组里面。

下来看看它的源码框架:

clean: function( elems, context, fragment, scripts ){var checkScriptType, script, j,ret = [];context = context || document;if ( typeof context.createElement === "undefined" ) {//这里处理了context为DOM元素,jQuery对象和其他情况context = context.ownerDocument || context[0] && context[0].ownerDocument || document;}//对每一个字符串进行处理for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {//元素为数字if ( typeof elem === "number" ) {elem += "";}//元素为undefined或者null或者''if ( !elem ) {continue;}//元素为字符串if ( typeof elem === "string" ) {}//在IE6/7中修正复选框单选按钮的选中状态var len;if ( !jQuery.support.appendChecked ) {if ( elem[0] && typeof (len = elem.length) === "number" ) {for ( j = 0; j < len; j++ ) {findInputs( elem[j] );}} else {findInputs( elem );}}if ( elem.nodeType ) {ret.push( elem );} else {ret = jQuery.merge( ret, elem );}}//元素为HTML代码片段if ( fragment ) {//去除掉script标签部分,其他标签部分插入文档流}return ret;
}

在clean函数源码中,主要是按照参数elems每个元素的类型来分类的,主要分了四大类:

对于数值类型: 转成字符串;
对于值为空的情况: 不做处理;
对于字符串,比较复杂,又要细分;
对于代码片段,将script标签部分分离出来,其他的标签插入到文档中;

下面主要分析参数为字符串时的源码:

if ( typeof elem === "string" ) {//正则表达式rhtml = /<|&#?\w+;/匹配标签,字符代码或字符代码if ( !rhtml.test( elem ) ) {//不是标签,创建文本节点elem = context.createTextNode( elem );} else {//匹配封闭标签//正则表达式rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig匹配除了自不封闭标签之外的标签//并调用字符串的replace方法,用匹配到的第一个分组和第二个分组来替换//比如:'<div/>'会被换成'<div></div>'elem = elem.replace(rxhtmlTag, "<$1></$2>");// 去除空格等,得到标签名var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),//获取最外层包裹元素,对option,legend,thead,tr,td,col,area特殊处理了//wrap 的格式是一个数组,[ 层级数 , 标签名(包含自带的上级标签), 封闭标签 ]wrap = wrapMap[ tag ] || wrapMap._default,depth = wrap[0], div = context.createElement("div"),safeChildNodes = safeFragment.childNodes,remove;// 将创建的div元素追加到html文档片段中if ( context === document ) {safeFragment.appendChild( div );} else {createSafeFragment( context ).appendChild( div );}// 将参数中的标签设为div的HTML文本,这样就将标签转换成了相应的DOM元素div.innerHTML = wrap[1] + elem + wrap[2];// 当elem为tr,td,option,legend等有默认父级标签时while ( depth-- ) {div = div.lastChild;}// 考虑IE浏览器中的tbody不兼容问题,这个暂时没有深究if ( !jQuery.support.tbody ) {var hasBody = rtbody.test(elem),tbody = tag === "table" && !hasBody ?div.firstChild && div.firstChild.childNodes :wrap[1] === "<table>" && !hasBody ?div.childNodes :[];for ( j = tbody.length - 1; j >= 0 ; --j ) {if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {tbody[ j ].parentNode.removeChild( tbody[ j ] );}}}if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );}elem = div.childNodes;//先获取到divd的子节点//再将初始创建的div元素删除掉,只保留后面追加的DOM元素if ( div ) {div.parentNode.removeChild( div );// Guard against -1 index exceptions in FF3.6if ( safeChildNodes.length > 0 ) {remove = safeChildNodes[ safeChildNodes.length - 1 ];if ( remove && remove.parentNode ) {remove.parentNode.removeChild( remove );}}}
}
}

今天主要就整理这么多,其中有些细节并没有完全掌握,不过对于buildFragment函数整体的框架已经有了了解。对于其中不具体或者不正确的部分,希望大家可以指出,欢迎批评指正!

这篇关于jQuery源码阅读(七)--init()遗留部分buildFragment()函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/959395

相关文章

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

css渐变色背景|<gradient示例详解

《css渐变色背景|<gradient示例详解》CSS渐变是一种从一种颜色平滑过渡到另一种颜色的效果,可以作为元素的背景,它包括线性渐变、径向渐变和锥形渐变,本文介绍css渐变色背景|<gradien... 使用渐变色作为背景可以直接将渐China编程变色用作元素的背景,可以看做是一种特殊的背景图片。(是作为背

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...

css实现图片旋转功能

《css实现图片旋转功能》:本文主要介绍了四种CSS变换效果:图片旋转90度、水平翻转、垂直翻转,并附带了相应的代码示例,详细内容请阅读本文,希望能对你有所帮助... 一 css实现图片旋转90度.icon{ -moz-transform:rotate(-90deg); -webkit-transfo