jQuery中的DOM操作通过jQuery源代码分析
1.版本问题
jQuery有三个大版本
- 1.x.x(兼容ie678),现在不提供更新
- 2.x.x(不兼容IE678),现在也不提供更新了
- 3.x.x (不兼容IE678),现在有更新
- 国内基本都使用1.x.x版本,原因就是兼容性的问题。 我下载的是1.x.x的最后一版(1.11.3)
其他还有一些介绍在我的另一篇博客中有介绍,这里就不赘述了。
jQuery介绍及各版本介绍(适合新手查看)
2.阅读源码
从这周二开始读jQuery,虽然才读了两千多行(一共10382行),但是已经有了很大的收获。其明晰的结构,高内聚、低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷、渐进增强)优雅的处理能力以及 Ajax 等方面周到而强大的定制功能无不令人惊叹。另外,阅读源码让我接触到了大量底层的知识。
1. 首先我们看看DOM操作有哪些
- 创建元素
- 插入元素
- 复制元素
- 替换元素
- 包裹元素
- 删除元素
2. 我们再来看看分别对应的方法是什么
创建元素
// $(htmlStr)
// htmlStr:html格式的字符串
$('<span-这是一个span元素</span-');
添加元素
append appendTo 在被选元素的结尾插入内容
prepend prependTo 在被选元素的开头插入内容
before 在被选元素之后插入内容
after 在被选元素之前插入内容
清空节点与删除节点
- empty:清空指定节点的所有元素,自身保留(清理门户)
$('div').empty(); // 清空div的所有内容(推荐使用,会清除子元素上绑定的内容,源码)
$('div').html('');// 使用html方法来清空元素,不推荐使用,会造成内存泄漏,绑定的事件不会被清除。
- remove:相比于empty,自身也删除(自杀)
$('div').remove();
复制元素
- 作用:复制匹配的元素
// 复制$(selector)所匹配到的元素(深度复制)
// cloneNode(true)
// 返回值为复制的新元素,和原来的元素没有任何关系了。即修改新元素,不会影响到原来的元素。
$(selector).clone();
替换元素
- 作用:替换页面中的元素
// 替换一共有两种方法
$(selector).replaceWith(content);//selector必选,表示要替换的元素,content必选,表示要替换的内容
$(selector).replaceAll(content);//用法于上面一样
包裹元素
- 作用:将页面中某个元素或者节点用其他元素包裹起来
$(selector).wrap(wrapper);//selector必选,表示要被包裹的元素或者节点,wrapper必选,可能的值有HTML代码,已有的元素,新元素;
再来看看jQuery中这些内容的源码
jQuery.fn.extend({//这里这么写的目的是直接通过jQuery($也可以调用)
text: function (value) {//设置内容
return access(this, function (value) {//下面是 条件?选择1:选择2的结构
return value === undefined ?
jQuery.text(this) :
this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(value));
}, null, value, arguments.length);
},
//这里是插入
append: function () {
return this.domManip(arguments, function (elem) {
if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
var target = manipulationTarget(this, elem);
target.appendChild(elem);
}
});
},
//插入到开头
prepend: function () {
return this.domManip(arguments, function (elem) {
if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
var target = manipulationTarget(this, elem);
target.insertBefore(elem, target.firstChild);
}
});
},
//插入到选中元素之前
before: function () {
return this.domManip(arguments, function (elem) {
if (this.parentNode) {
this.parentNode.insertBefore(elem, this);
}
});
},
//插入到选中元素之后
after: function () {
return this.domManip(arguments, function (elem) {
if (this.parentNode) {
this.parentNode.insertBefore(elem, this.nextSibling);
}
});
},
//移除元素
remove: function (selector, keepData /* Internal Use Only */ ) {
var elem,
elems = selector ? jQuery.filter(selector, this) : this,
i = 0;
for (;
(elem = elems[i]) != null; i++) {
if (!keepData && elem.nodeType === 1) {
jQuery.cleanData(getAll(elem));
}
if (elem.parentNode) {
if (keepData && jQuery.contains(elem.ownerDocument, elem)) {
setGlobalEval(getAll(elem, "script"));
}
elem.parentNode.removeChild(elem);
}
}
return this;
},
//也是移除元素
empty: function () {
var elem,
i = 0;
for (;
(elem = this[i]) != null; i++) {
// Remove element nodes and prevent memory leaks
// 删除元素节点并防止内存泄漏
if (elem.nodeType === 1) {
jQuery.cleanData(getAll(elem, false));
}
// Remove any remaining nodes
// 删除所有剩余节点
while (elem.firstChild) {
elem.removeChild(elem.firstChild);
}
// If this is a select, ensure that it displays empty (#12336)
// 如果这是选择,请确保它显示为空(#12336)
// Support: IE<9
if (elem.options && jQuery.nodeName(elem, "select")) {
elem.options.length = 0;
}
}
return this;
},
//复制元素
clone: function (dataAndEvents, deepDataAndEvents) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map(function () {
return jQuery.clone(this, dataAndEvents, deepDataAndEvents);
});
},
//设置内容
html: function (value) {
return access(this, function (value) {
var elem = this[0] || {},
i = 0,
l = this.length;
if (value === undefined) {
return elem.nodeType === 1 ?
elem.innerHTML.replace(rinlinejQuery, "") :
undefined;
}
// See if we can take a shortcut and just use innerHTML
// 看看我们是否可以采取快捷方式,只是使用内部HTML
if (typeof value === "string" && !rnoInnerhtml.test(value) &&
(support.htmlSerialize || !rnoshimcache.test(value)) &&
(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
// 如果使用内部HTML引发异常,请使用回退方法
} catch (e) {}
}
if (elem) {
this.empty().append(value);
}
}, null, value, arguments.length);
},
//替换元素
replaceWith: function () {
var arg = arguments[0];
// Make the changes, replacing each context element with the new content
// 进行更改,将每个上下文元素替换为新内容
this.domManip(arguments, function (elem) {
arg = this.parentNode;
jQuery.cleanData(getAll(this));
if (arg) {
arg.replaceChild(elem, this);
}
});
// Force removal if there was no new content (e.g., from empty arguments)
// 如果没有新内容(例如,从空参数删除)
return arg && (arg.length || arg.nodeType) ? this : this.remove();
},
//删除元素
detach: function (selector) {
return this.remove(selector, true);
},
});
笔者资历尚浅,如有其他见解欢迎指点。
推荐阅读:
JS的设计模式——观察者设计模式