jQuery 源码分析(二十一) DOM操作模块 删除元素 详解
本节说一下DOM操作模块里的删除元素模块,该模块用于删除DOM里的某个节点,也可以理解为将该节点从DOM树中卸载掉,如果该节点有绑定事件,我们可以选择保留或删除这些事件,删除元素的接口有如下三个:
- empty() ;移除匹配元素的所有子元素。 ;先移除所有后代元素关联的数据和事件,以避免内存泄漏。然后移除子元素。
- remove(selector,keepData) ;从匹配元素中移除selector元素。 ;selector是可选的选择器表达式,如果未传入则全部移除,否则只移除对应的匹配元素。keepData可选,表示是否保留匹配元素以及它的后代元素所关联的数据和事件
- detach(selector) ;从文档中移除匹配元素集合 ;但会保留后代元素和匹配元素关联的数据和事件,常用于移除的元素稍后再次插入文档的场景。
说直接一点,
- empty()会直接移除当前匹配的元素
- remove()会将当前匹配的元素中在匹配参数1selector里的元素,如果参数2keepData为true则保存该DOM节点的事件,否则移除事件
- detach()是调用remove()实现的,用于移除当前匹配的元素,但是会保留数据 ,它是调用remove(selector,true)来实现的
writer by:大沙漠 QQ:22969969
举个栗子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
<div>
<h1>你好</h1>
</div>
<p>朋友</p>
<button id="b1">测试1</button>
<button id="b2">测试2</button>
<button id="b3">测试3</button>
<script>
$('h1').click(function(){console.log('h1 click')}); //给h1元素增加一个点击事件
$('p').click(function(){console.log('p click')}); //给P元素增加一个点击事件 //以下给三个按钮都会实现将h1、p元素从DOM树中移除,再插入body的子节点的前面,不同的地方是点击子节点时的输出信息不同
b1.onclick=function(){$('h1,p').remove().prependTo('body')} //点击测试1按钮后 h1和p元素都移动到了body子节点的最前面,但是再点击h1和p元素都没有反应
b2.onclick=function(){$('h1,p').remove('p').prependTo('body')} //点击测试1按钮后 h1和p元素都移动到了body子节点的最前面,点击h1有输出,点击p没有输出
b3.onclick=function(){$('h1,p').detach().prependTo('body')} //点击测试1按钮后 h1和p元素都移动到了body子节点的最前面,点击h1和p都有输出
</script>
</body>
</html>
渲染如下:

对应的DOM树如下:

我们给h1和p元素分别绑定了一个事件,点击分别输出:h1 click和p click,,点击输出如下:

另外不管点击测试1、测试2还是测试3,都会将h1和p元素从DOM树中卸载并插入到body子节点的最前面,如下:

此时再点击h1和p元素就会有不同的输出了:
- 对于测试1按钮来说 ;点击h1和p没有任何输出 ;由于调用了remove()方法且未传递参数,所以所有匹配的元素的事件都会被删除掉
- 对于测试2按钮来说 ;点击h1有输出,点击p没有输出 ;由于调用remove()方法时传递了p,所以把p元素的事件都删除了,而h1元素的事件并没有被删除
- 对于测试2按钮来说 ;点击h1和p没有任何输出 ;由于调用了detach()方法,所以所有元素的数据都被保留了,detach()内部时调用remove( selector, true )来实现的,也就是remove的参数2为true
源码分析
讲解empty()、remove()和detach()之前先介绍一个jQuery的API,就是$.cleanData(elems),参数是DOM节点的集合(可以是document.getElementsByTagName的返回值),它用于移除一个DOM节点上绑定的jQuery事件的,我们举个栗子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
<p>Hello World</p>
<button>测试</button>
<script>
$('p').click(function(){console.log('p click')}); //给P元素增加一个点击事件
$('button').click(function(){
$.cleanData(document.getElementsByTagName('p')) //给button按钮添加一个button事件,内部调用$.cleanData去清楚p元素上绑定的事件
})
</script>
</body>
</html>
渲染如下:

初始化时我们点击Hello World控制台会输出信息,如果点击按钮后再点击Hello World就不会输出了,因为$.cleanData把该DOM元素的事件给卸载了,$.cleanData实现如下:
jQuery.extend({
cleanData: function( elems ) { //移除多个DOM元素的全部数据和事件, elems是待移除数据和事件的DOM元素数组。
var data, id,
cache = jQuery.cache,
special = jQuery.event.special,
deleteExpando = jQuery.support.deleteExpando;
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { //遍历每个匹配元素
if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { //如果不支持设置属性,则跳过本次循环。 jQuery.noData是一个数组,保存了不支持扩展属性的节点名称。
continue;
}
id = elem[ jQuery.expando ]; //属性名,注1
if ( id ) { //如果该DOM对象有jQuery.expando属性,则表示设置过数据。
data = cache[ id ];
if ( data && data.events ) { //如果DOM的关联数据缓存对象存在且含有events属性,说明在该DOM上绑定过事件,则移除
for ( var type in data.events ) { //data.events是DOM元素的事件缓存对象,存储了DOM元素的所有事件
if ( special[ type ] ) {
jQuery.event.remove( elem, type );
// This is a shortcut to avoid jQuery.event.remove's overhead
} else {
jQuery.removeEvent( elem, type, data.handle );
}
}
// Null the DOM reference to avoid IE6/7/8 leak (#7054)
if ( data.handle ) {
data.handle.elem = null;
}
}
if ( deleteExpando ) { //如果jQuery.support.deleteExpando;返回ture,即浏览器支持删除DOM元素上的扩展属性
delete elem[ jQuery.expando ]; //删除DOM元素的jQuery.expando属性
} else if ( elem.removeAttribute ) { //如果浏览器不支持删除DOM元素上的扩展属性,则调用removeAttribute删除属性
elem.removeAttribute( jQuery.expando );
}
delete cache[ id ]; //删除DOM元素的数据缓存对象cache[id]
}
}
}
/*略*/
})
注1:jQuery绑定的事件对应的信息都存在$.cache里,键名是对应DOM对象的jQuery.expando,也就是这里的elem[ jQuery.expando ],有疑问可以看看之前事件的讲解:https://www.cnblogs.com/greatdesert/p/11679334.html
cleanData就是删除参数elems里所有DOM元素的事件,本节的empty()和remove()都是基于cleanData实现的,empty()实现如下:
jQuery.fn.extend({
empty: function() { //从文档中移除匹配元素的所有子元素。
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { //遍历当前匹配的每个元素
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") ); ////先移除所有后代元素关联的数据和事件,以避免内存泄漏
}
// Remove any remaining nodes
while ( elem.firstChild ) { //然后移除子元素。
elem.removeChild( elem.firstChild );
}
}
return this;
},
})
empty()内首先调用cleanData()移除所有子孙节点的数据和事件,然后通过一个while()循环卸载掉每个DOM节点,比较好理解哈,对于remove()来说,实现如下:
jQuery.fn.extend({
remove: function( selector, keepData ) { //从匹配元素中移除selector元素,该方法会遍历匹配元素集合,先删除后代元素和匹配元素的数据和事件,以避免内存泄漏,然后移除匹配元素。
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { //遍历当前匹配元素
if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { //如果没有传入selector参数,或者传入了selector参数,并且当前元素与之匹配
if ( !keepData && elem.nodeType === 1 ) { //如果没有传入keepData参数且elem是元素节点
jQuery.cleanData( elem.getElementsByTagName("*") ); //移除后代元素的全部数据和事件
jQuery.cleanData( [ elem ] ); //移除元素节点。
}
if ( elem.parentNode ) { //调用原生方法removeChild()移除节点。
elem.parentNode.removeChild( elem );
}
}
}
return this;
},
detach: function( selector ) {
return this.remove( selector, true ); //调用remove()来实现的,参数2传入了true
},
})
remove()方法会根据selector来选择匹配元素,并将该匹配元素从DOM树中卸载,如果有传入keepData则保存数据,否则将数据删除,对于detach来说,就是调用remove()来实现的
jQuery 源码分析(二十一) DOM操作模块 删除元素 详解的更多相关文章
- jQuery 源码分析(二十) DOM操作模块 插入元素 详解
jQuery的DOM操作模块封装了DOM模型的insertBefore().appendChild().removeChild().cloneNode().replaceChild()等原生方法.分为 ...
- jQuery 源码解析(二十三) DOM操作模块 替换元素 详解
本节说一下DOM操作模块里的替换元素模块,该模块可将当前匹配的元素替换指定的DOM元素,有两个方法,如下: replaceWith(value) ;使用提供的新内容来替换匹配元素集合中的每个元 ...
- jQuery 源码解析(二十四) DOM操作模块 包裹元素 详解
本节说一下DOM操作模块里的包裹元素子模块,该模块可将当前匹配的元素替换指定的DOM元素,有如下方法: wrap(html) ;在每个匹配元素的外层添加一层DOM元素 ...
- jQuery 源码解析(二十二) DOM操作模块 复制元素 详解
本节说一下DOM操作模块里的复制元素子模块,该模块可以复制一个DOM节点,并且可选择的设置是否复制其数据缓存对象(包含事件信息)和是否深度复制(子孙节点等),API如下: $.clone(elem, ...
- jQuery 源码解析(二十七) 样式操作模块 坐标详解
样式操作模块可用于管理DOM元素的样式.坐标和尺寸,本节讲解一下坐标这一块. 对于坐标来说,jQuery提供了一个offset方法用于获取第一个匹配元素的坐标或者设置所有匹配元素的坐标,还有offse ...
- jQuery 源码分析(十九) DOM遍历模块详解
jQuery的DOM遍历模块对DOM模型的原生属性parentNode.childNodes.firstChild.lastChild.previousSibling.nextSibling进行了封装 ...
- jQuery 源码分析(十五) 数据操作模块 val详解
jQuery的属性操作模块总共有4个部分,本篇说一下最后一个部分:val值的操作,也是属性操作里最简单的吧,只有一个API,如下: val(vlaue) ;获取匹配元素集合中第一个元素的 ...
- jQuery 源码分析(十四) 数据操作模块 类样式操作 详解
jQuery的属性操作模块总共有4个部分,本篇说一下第3个部分:类样式操作部分,用于修改DOM元素的class特性的,对于类样式操作来说,jQuery并没有定义静态方法,而只定义了实例方法,如下: a ...
- Vue.js 源码分析(二十九) 高级应用 transition-group组件 详解
对于过度动画如果要同时渲染整个列表时,可以使用transition-group组件. transition-group组件的props和transition组件类似,不同点是transition-gr ...
随机推荐
- 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解
写在前面的话 相关背景及资源: 曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享 工程代码地址 思维导图地址 工程结构图: 正 ...
- eclipse 代码问题总结
隐藏控件,在xml文件中写属性 android:visibility="gone"
- Winform DataGridView控件数据导出EXcel
/// <summary> /// 导出Excel /// </summary> /// <param name="dgv"></para ...
- java8-Stream流API
一回顾与说明 经过前面发布的三章java8的博客,你就懂得了我们为什么要用Lamda表达式,Lamda表达式的原理与函数式接口的关系,从Lamda表达式到方法引用和构造引用. 想要学Stream流你必 ...
- 《Java练习题》进阶练习题(五)
编程合集: https://www.cnblogs.com/jssj/p/12002760.html 前言:不仅仅要实现,更要提升性能,精益求精,用尽量少的时间复杂度和空间复杂度解决问题. [程序88 ...
- CentOS7自动化安装PXE方案
目的 无人值守批量安装CentOS7 安装条件 一台带有PXE协议支持NIC的待安装主机 一台存放安装文件的服务器,如NFS,HTTP或FTP服务器 Kickstart 生成的配置文件(ks.cfg) ...
- delete和Vue.$delete删除数组的区别
delete delete只是被删除的元素变成了 empty/undefined ,其他的元素的键值对应关系还是不变. Vue.$delete 直接删除了数组中元素,数组的键值对应关系发生相应变化 例 ...
- 《Web Development with Go》Mangodb查询一条记录
select加where package main import ( "fmt" "log" "time" "gopkg.in/m ...
- React的世界观及与Vue之比较
写在前面:本文谈论的是主观的个人感受,不追求立场的“客观.公正”,因此我下面所说的很可能是错的,欢迎交流指正. 我学习前端时,跟大部分beginner一样,学的第一个框架是Vue,入职后也一直写Vue ...
- Python爬虫基础面试题,为2020年初就业做准备
前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:qiaoziheng 一.题目部分1.python中常用的数据结构有哪些 ...