本文介绍如何利用JavaScript原型链扩展,手写实现类似jQuery的常用DOM操作方法。涵盖显示隐藏、滑动动画、属性与样式设置、元素增删改、CSS类管理以及基础选择器功能。通过直接操作DOM对象并返回当前实例,实现链式调用,帮助开发者在无需依赖框架的情况下掌握底层DOM操作原理。
引言与原型扩展思路
近期专注于原生JavaScript开发,在无需依赖jQuery等框架的场景下,倾向于手动编写JS逻辑。回顾早期前端开发经历,往往为了获取选择器功能而不得不引入jQuery。如今,通过扩展对象原型的方式,可以复现部分jQuery的核心函数功能。
基础DOM操作:显示、隐藏与滑动
1. 显示与隐藏元素
//hide()
Object.prototype.hide = function(){
this.style.display="none";
return this;
}
//show()
Object.prototype.show = function(){
this.style.display="block";
return this;
}
此处返回this的关键作用在于支持链式调用,使得多个方法可以连续执行。
2. 滑动效果
//slideDown()
Object.prototype.slideDown = function(){
this.style.display = 'block';
if(this.clientHeight<this.scrollHeight){
this.style.height=10+this.clientHeight+"px";
var _this = this;
setTimeout(function(){_this.slideDown()},10)
}else{
this.style.height=this.scrollHeight+"px";
}
}
//slideUp()
Object.prototype.slideUp = function(){
if(this.clientHeight>0){
this.style.height=this.clientHeight-10+"px";
var _this = this;
setTimeout(function(){_this.slideUp()},10)
}else{
this.style.height=0;
this.style.display = 'none';
}
}
属性与样式管理
3. 属性获取与设置
//attr()
Object.prototype.attr = function(){
if(arguments.length==1){
return eval("this."+arguments[0]);
}else if(arguments.length==2){
eval("this."+arguments[0]+"="+arguments[1]);
return this;
}
}
//val()
Object.prototype.val = function(){
if(arguments.length==0){
return this.value;
}else if(arguments.length==1){
this.value = arguments[0];
return this;
}
}
//html()
Object.prototype.html = function(){
if(arguments.length==0){
return this.innerHTML;
}else if(arguments.length==1){
this.innerHTML = arguments[0];
return this;
}
}
//text()需要在html()结果基础上排除标签,会很长,省略
4. CSS样式处理
//css()
Object.prototype.css = function(){
if(arguments.length==1){
return eval("this.style."+arguments[0]);
}else if(arguments.length==2){
eval("this.style."+arguments[0]+"='"+arguments[1]+"'");
return this;
}
}
元素增删改与类名控制
5. 元素添加操作
//append()
Object.prototype.append = function(newElem){
this.innerHTML += newElem;
return this;
}
//prepend()
Object.prototype.prepend = function(newElem){
this.innerHTML = arguments[0] + this.innerHTML;
return this;
}
//after()
Object.prototype.after = function(newElem){
this.outerHTML += arguments[0];
return this;
}
//before()
Object.prototype.before = function(newElem){
this.outerHTML = arguments[0] + this.outerHTML;
return this;
}
6. 元素删除与替换
//empty()
Object.prototype.empty = function(){
this.innerHTML = "";
return this;
}
//replaceWith()
Object.prototype.replaceWith = function(newElem){
this.outerHTML = arguments[0];
return this;
}
//remove() js自带,省略。
7. CSS类名管理
//hasClass()
Object.prototype.hasClass = function(cName){
return !!this.className.match( new RegExp( "(\\s|^)" + cName + "(\\s|$)") );
}
//addClass()
Object.prototype.addClass = function(cName){
if( !this.hasClass( cName ) ){
this.className += " " + cName;
}
return this;
}
//removeClass()
Object.prototype.removeClass = function(cName){
if( this.hasClass( cName ) ){
this.className = this.className.replace( new RegExp( "(\\s|^)" + cName + "(\\s|$)" )," " );
}
return this;
}
上述CSS类名的设置逻辑,亦可借助HTML5新增的classList API及contains方法来实现。需要注意的是,该方式在IE8及以下版本及部分旧版Firefox浏览器中存在兼容性问题。
Object.prototype.hasClass = function(cName){
return this.classList.contains(cName)
}
Object.prototype.addClass = function(cName){
if( !this.hasClass( cName ) ){
this.classList.add(cName);
}
return this;
}
Object.prototype.removeClass = function(cName){
if( this.hasClass( cName ) ){
this.classList.remove(cName);
}
return this;
}
选择器机制与遍历处理
9. 选择器功能
//id或class选择器$("elem")
function $(strExpr){
var idExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/;
var classExpr = /^(?:\s*(<[\w\W]+>)[^>]*|.([\w-]*))$/;
if(idExpr.test(strExpr)){
var idMatch = idExpr.exec(strExpr);
return document.getElementById(idMatch[2]);
}else if(classExpr.test(strExpr)){
var classMatch = classExpr.exec(strExpr);
var allElement = document.getElementsByTagName("*");
var ClassMatch = [];
for(var i=0,l=allElement.length; i<l; i++){
if(allElement[i].className.match( new RegExp( "(\\s|^)" + classMatch[2] + "(\\s|$)") )){
ClassMatch.push(allElement[i]);
}
}
return ClassMatch;
}
}
需要特别说明的是,上述选择器返回的结果或结果集包含的是HTML DOM对象,而非jQuery封装的对象实例。多数开发者知晓,document.getElementById("id")的执行结果等价于jQuery选择器$("#id")[0]。此外,若需使用上述class选择器获取的结果,必须结合forEach方法进行遍历处理。
$(".cls").forEach(function(e){
e.css("background","#f6f6f6")
})
10. 遍历操作
//siblings()
Object.prototype.siblings = function(){
var chid=this.parentNode.children;
var eleMatch = [];
for(var i=0,l=chid.length;i<l;i++){
if(chid[i]!=this){
eleMatch.push(chid[i]);
}
}
return eleMatch;
}
//children() 原生js已含有该方法,故命名为userChildren。
Object.prototype.userChildren = function(){
var chid=this.childNodes;
var eleMatch = [];
for(var i=0,l=chid.length;i<l;i++){
eleMatch.push(chid[i]);
}
return eleMatch;
}
//parent()
Object.prototype.parent = function(){
return this.parentNode;
}
//next()
Object.prototype.next = function(){
return this.nextElementSibling;
}
//prev()
Object.prototype.prev = function(){
return this.previousElementSibling;
}
总结与后续展望
jQuery中的事件处理函数在原生JS中已原生支持。另外,关于原生JS实现jQuery中常用的ajax功能,将在后续文章中详细阐述。
原生JS实现ajax方法
以上即为小编为大家整理的原生JS模拟jQuery常用方法的全部内容,希望能为读者提供帮助,欢迎继续支持本站。