jQuery源码-dom操作之jQuery.fn.html

简介:

写在前面

前面陆陆续续写了jQuery源码的一些分析,尽可能地想要cover里面的源码细节,结果导致进度有些缓慢。jQuery的源码本来就比较晦涩,里面还有很多为了解决兼容问题很引入的神代码,如果不google的话压根不知道那一段段代码为什么会存在于人世。

于是就一直在重复坐着这么件事情,到处谷歌或者请教别人,这段兼容代码是为解决神马问题引入的。好不容易把所有的源码细节搞清楚,喝着咖啡对着电脑欣赏自己的劳动成果,内心却闪过一丝奇怪的感觉:我花了这么长的时间究竟做了什么?就为了搞清楚这段常理无法解释的代码?而在这之前已经有无数仁人志士在这上面浪费了自己多少宝贵的时间。

当然,学习jQuery源码对于我这种老菜鸟还是很有助益的,只不过需要换种方式,不再去抠一些无谓的细节了,有些比较难理解的东西就直接扔出来看下园里的朋友们帮忙解答下了。社会化写作似乎是更好的方式,之前也想过把系列文章扔github让别人来帮忙完善,不过显然对于大部分人来说这种方式成本还是太高,而自己写的东西暂时也没有说让人家去fork然后pull request的价值,就作罢了。

技术无关的内容就此打住,还是老老实实开始我的源码分析。里面标疑惑的地方,是还没来得及去抠的细节(一般就是一些正则神马的),围观的群众如果能够帮忙解答下那是真真的好~~

简单例子

 jQuery.fn.html()同样属于使用频率比较高的接口,从它的接口文档http://api.jquery.com/html/,可知有如下几种用法,假设有如下HTML片段

<div id="casper">
    <span>name:</span>
    <span>casper</span>
</div>

读取:$(selector).html()

运行下面代码

console.log( $('#casper').html() );

输出:

    <span>name:</span>
    <span>casper</span>

设置一:$(selector).html(value)

还是上面的HTML,运行下面脚本

$('#casper').html('<p>大家好,我是第一段文本</p><p>大家好,我是第二段文本</p>');

原本的HTML变成

<div id="casper"><p>大家好,我是第一段文本</p><p>大家好,我是第二段文本</p></div>

设置二:$(selector).html(callback)

在上面例子中,HTML变成如下

<div id="casper"><p>大家好,我是第一段文本</p><p>大家好,我是第二段文本</p></div>

运行如下代码,参数 index、html 分别代码什么,见下面输出即可,不赘述

$('#casper p').html(function(index, html){
    return index + '、原本的内容:'+ html;
});

结果原来的HTML变成(为方便查看进行了适当格式化)

<div id="casper">
    <p>0、原本的内容:大家好,我是第一段文本</p>
    <p>1、原本的内容:大家好,我是第二段文本</p>
</div>

源码分析

下面湿jQuery.fn.html 的源码,就直接贴上来了,一点都不意外,又见到了全知全能的jQuery.access方法。。。这里我们先不立即展开,下文慢慢分析

  jQuery.fn.html源码

把传入jQuery.access的会掉方法的方法体去掉,我们看到如下代码,就是调用了下jQuery.access而已(这里我们还不知道jQuery.access那噩梦般的参数究竟是干嘛的):

复制代码
html: function( value ) {
    return jQuery.access( this, function( value ) {
        
        // 先隐藏掉方法体内的细节
    
    }, null, value, arguments.length );
},
复制代码

实在不知道如何下手分析jQuery.access,就直接跳过jQuery.access运行的内部细节,给出各种情况下的运行分支

源码分析之:fn被调用的各种情况

假设在jQuery.fn.html方法中,第二个传入的回调方法为fn

读取:$(selector).html()

html: function( value ) {
    return fn.call(this);
},

设置一:$(selector).html(value)

html: function( value ) {
    fn.call( this, value );
    return this;
},

设置二:$(selector).html(callback)

这里可能比较费解一点,得结合设置一的代码来看(其实结合了业不容易看懂),可以看到,内部比较曲折,最终将$(selector).html(callback)转成了$(selector).html(value)来实现,这种转换手法在jQuery源码里很常见。

复制代码
html: function( callback ) {
    var elems = this,
        i = 0,
        length = elems.length;

    var bulk = fn;
  // elem为dom元素,value为该dom元素最初的innerHTML
    fn = function( elem, value ) {
        return bulk.call( jQuery( elem ), value );
    };
    for ( ; i < length; i++ ) {
    // 第一步:fn( elems[i], key ) ) 返回elem[i]的innerHTML,相当于 $(elem[i]).html()
        // 第二步:callback.call( elems[i], i, fn( elems[i], key ) ),就是将 i、$(elem[i]).html()当参数传给 callback
        // 第三步:fn( elems[i], XXXX ) 到了这一步,其实就是 $(elem[i]).html(value)的等价形式了,因为第三步中已经返回了一段html文本
        fn( elems[i], callback.call( elems[i], i, fn( elems[i], key ) ) );
    }

    return this;
}
复制代码

源码分析之:fn各种情况下内部的分支逻辑

接下来到大头,上面说的fn的源码了,下面列出各种情况下,fn内部的处理逻辑

读取:$(selector).html()

fn里面的处理逻辑,很简单,将$(selector)选中的第一个dom元素的innerHTML属性返回。

疑问:rinlinejQuery这个正则是干嘛?为什么还要将elem.innerHTML先替换一下再返回?

复制代码
var elem = this[0] || {},  // this为选中的jQuery对象
    i = 0,
    l = this.length;

if ( value === undefined ) {
    return elem.nodeType === 1 ?
        elem.innerHTML.replace( rinlinejQuery, "" ) :
        undefined;
}
复制代码

设置一:$(selector).html(value)

代码也不算复杂,就是遍历下$(selector)选中的元素,将它们的innerHTML分别设置成value。这里麻烦点在于,value存在,还得符合一堆条件才能走进这个if,原谅我偷懒了

疑问:

!rnoInnerhtml.test( value ):判断神马的

( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ):判断神马的

( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ):判断神马的

!wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ]:判断神马的

value = value.replace( rxhtmlTag, "<$1></$2>" ); 作用是神马

复制代码
// See if we can take a shortcut and just use innerHTML
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
    ( jQuery.support.htmlSerialize || !rnoshimcache.test( value )  ) &&
    ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
    !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {

    value = value.replace( rxhtmlTag, "<$1></$2>" );

    try {
        for (; i < l; i++ ) {
            // Remove element nodes and prevent memory leaks
            elem = this[i] || {};
            if ( elem.nodeType === 1 ) {
                jQuery.cleanData( getAll( elem, false ) );
                elem.innerHTML = value;
            }
        }

        elem = 0;

    // If using innerHTML throws an exception, use the fallback method
    } catch(e) {}
}
复制代码

设置二:$(selector).html(callback)

 这里直接略过,因为fn内部的逻辑是跟$(selector).html(value)一样的

写在后面

本文有些偷懒,凑合着看吧。。里面提到的疑惑,如果能够帮忙解答下就更好了。。。


相关文章
|
1月前
|
JavaScript 前端开发
使用jQuery操作DOM元素
使用jQuery操作DOM元素
16 1
|
1月前
|
JavaScript 前端开发
JavaScript操作DOM元素
JavaScript操作DOM元素
12 1
|
2天前
|
存储 JavaScript 数据可视化
vue3+echarts应用——深度遍历html的dom结构并用树图进行可视化
vue3+echarts应用——深度遍历html的dom结构并用树图进行可视化
14 1
|
6天前
|
JavaScript 前端开发 UED
深入解析JavaScript原生操作DOM技术
【4月更文挑战第22天】本文深入探讨JavaScript原生DOM操作技术,包括使用`getElement*`方法和CSS选择器获取元素,借助`createElement`与`appendChild`动态创建及插入元素,修改元素内容、属性和样式,以及删除元素。通过掌握这些技术,开发者能实现页面动态交互,但应注意避免过度操作DOM以优化性能和用户体验。
|
13天前
|
存储 JavaScript 前端开发
JavaScript DOM 操作:解释一下 cookie、sessionStorage 和 localStorage 的区别。
Cookie是服务器发送至客户端的文本信息,会随每个请求发送回服务器,适合控制会话状态但可能暴露隐私。SessionStorage仅在当前会话中存储数据,关闭浏览器后清除,适合临时存储如登录状态。LocalStorage则持久保存数据,即使关闭浏览器也不会清除,适用于存储长期设置。三种方式各有侧重,应按需求选择。
16 0
|
13天前
|
JavaScript 前端开发 安全
JavaScript DOM 操作:解释一下浏览器的同源策略。
**同源策略**是浏览器安全基石,它阻止脚本跨不同协议、域名或端口访问资源,防止恶意行为。例如,HTTP页面无法直接用JS获取HTTPS页面内容。**CORS**允许跨域请求,但需服务器配合设置,通过`document.domain`属性可配置,但仍受限于服务器配置。
14 4
N..
|
1月前
|
JavaScript 前端开发 容器
jQuery中的DOM操作
jQuery中的DOM操作
N..
14 1
|
1月前
|
JavaScript
jQuery选择器案例之——index.html
jQuery选择器案例之——index.html
9 1
|
1月前
|
前端开发 JavaScript Java
第四章使用jQuery操作DOM元素
第四章使用jQuery操作DOM元素
9 0
|
1月前
|
JavaScript 前端开发
如何使用 JavaScript 操作 DOM?
如何使用 JavaScript 操作 DOM?
15 0