目录
1.课堂主题
2.知识点
3.jquery特性说明
3.1处理ready和原生节点
3.2封装jq的eq方法及preObject属性
3.3实现jq里的click方法
3.4实现on方法
3.5实现css方法
3.6自定义实现$.cssNumber
3.7自定义实现$.cssHooks
4.总结及完整案例
1.课堂主题
- 定义函数返还JQuery对象
- ready方法和原生节点处理
- 选择器?封装
- 封装JQ的eq方法
- 封装JQ的click方法
- 封装JQ的on方法
- 封装JQ的css方法
2.知识点
- 对象成员与类成员
- 判断类型
- 链式调用实现
- 正则表达式
3.jquery特性说明
知己知彼,百战不殆;想要知道jq功能如何实现,先要了解其特性;
- jquery中有各种方法:eq(),click(),on(),css()等;
- jquery各方法间可以实现链式操作;
jquery原方法示意:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script src="jquery-3.4.1_20190825_115323.js"></script><title>jquery特性说明</title><style>div {width: 100px;height: 100px; }#mydiv1 {background-color: red;}#mydiv2 {background-color: blue;}</style>
</head>
<body><div id="mydiv1"></div><div id="mydiv2"></div><script>// $()的参数可以是字符串/函数/原生节点三种形式console.log( "参数为字符串",$("div"));$(function(){console.log("参数为函数");});console.log( "参数为DOM操作",$(document.querySelectorAll("div")));// $()方法返回的是一个jquery对象console.log($("div"));//jQuery.fn.init(2) [div#mydiv1, div#mydiv2, prevObject: jQuery.fn.init(1)]// jquery对象上click()方法$("div").click(function(){console.log("div -----click");});// jquery对象上eq()方法$("div").eq(0).click(function(){console.log("第一个div -----click");});// jquery对象上on()方法$("div").on("mouseover mouseout",function(){console.log("鼠标移入移出");});// jquery对象上css()方法:有三种方式css("width","200px")设置一个样式 css("width")获取样式 css({"width:100px","height:100px"}})设置多个样式$("div").eq(0).css("width","200px");console.log($("div").eq(0).css("width"));//200px$("div").eq(1).css({"background-color":"yellow","height":"200px"});// jquery对象上preObject属性:记录上一次操作的对象;end()获取上一次操作节点console.log($("div").eq(0).end());//jQuery.fn.init(2) [div#mydiv1, div#mydiv2, prevObject: jQuery.fn.init(1)]// cssNumber单位容错// cssHooks()自定义扩展样式$.cssHooks["wh"] = {get(ele){console.log("get()-----");console.log("cssHooks获取后",ele.style["width"]);},set(ele,value){ele.style["width"] = value;ele.style["height"] = value;}};// cssHooks设置:自定义扩展$("#mydiv1").css("wh","200px");$("#mydiv1").css("wh");</script>
</body>
</html>
###如何实现链式调用
根据对象的特性,$()函数返还jq对象;且$()参数有三种形式:字符串,函数,原生节点。
// console.log($("div"));// 传入参数为原生节点:参数个数可为一个/多个/数组console.log($(document.querySelectorAll("div")));//NodeList(2) [div#mydiv1, div#mydiv2]// $(function(){// console.log("函数");// });
class Jq{constructor(ele){//$()参数通过typeof可知有三种形式:字符串,函数,NodeList。if(typeof ele === "string"){//如果为string类型,返回获取到的DOM对象(注意获取的可能为多个,所以需要循环)let eles = document.querySelectorAll(ele); this.addDom(eles); }else if(typeof ele === "function"){// 当ele为function时,将其挂载到window的监听事件DOMContentLoaded上window.addEventListener("DOMContentLoaded",ele);}else{// 当参数为原生节点时:如果typeof ele.length为undefined即代表只有一个原生节点,否则类型为Objectif(typeof ele.length === "undefined"){this[0] = ele;this.length = 1;}else{this.addDom(ele); //有多个原生节点 }}}//添加节点addDom(eles){//循环将节点添加到对象上eles.forEach((element,index) => {this[index] = element;});//返回有对象的length属性this.length = eles.length;}
}//返回的是整个Jq对象
function $(ele){return new Jq(ele);//$()函数返还jq对象
}
3.1处理ready和原生节点
- 针对ready方法做兼容处理
// 当ele为function时,将其挂载到window的监听事件DOMContentLoaded上window.addEventListener("DOMContentLoaded",ele);
- 原生节点直接保存
// 当参数为原生节点时:如果typeof ele.length为undefined即代表只有一个原生节点,否则类型为Objectif(typeof ele.length === "undefined"){this[0] = ele;this.length = 1;}else{this.addDom(ele); //有多个原生节点 }
###选择器?封装
3.2封装jq的eq方法及preObject属性
注意返还对象,实现链式调用
###实现end方法
返还上次操作的节点;
- preObject属性:注意preObject属性表示上一次操作的节点,如果不再有上一次操作的节点就返回document(注意也是Jq对象),使用end()方法获取上一次操作的节点;
- 通过root标识第一次赋值document。只有第一次root没有赋值时才会为undefined,其他时候都会赋值为null;
//endconsole.log($("div").eq(1).end());//Jq {0: div#mydiv1, 1: div#mydiv2, length: 2}
constructor(ele,root){// preObject属性:注意preObject属性表示上一次操作的节点,如果不再有上一次操作的节点就返回document(注意也是Jq对象),使用end()方法获取上一次操作的节点// 通过root标识第一次赋值document。只有第一次root没有赋值时才会为undefined,其他时候都会赋值为nullif(root === "undefined"){this.preObject = new Jq(document,null);}if(root){this.preObject = root;}
}// eq()即获取对象上的第几个节点,返回的是一个对象eq(index){return new Jq(this[index],this);//此处this代表整个Jq对象}// end()方法:用于获取上一次操作的节点即this.preObjectend(){return this.preObject;}
3.3实现jq里的click方法
绑定click事件。注意this.length代表所有的节点数,通过循环this.length即可循环执行所有节点
// click$("div").click(function(){console.log("click div");});
//click:注意this.length代表所有的节点数,通过循环this.length即可循环执行所有节点click(func){for(let i=0;i<this.length;i++){this[i].addEventListener("click",func)}}
3.4实现on方法
和click方法类似,可以处理多个事件;
注意:空格去重,事件转为小写
// on$("div").on("mouseOver moUseout",function(){console.log("执行on");});
//on():可以绑定多个事件,事件之间通过空格隔开可能存在多个空格需要去重、需要忽略事件的大小写on(event,func){// 通过replace()方法去掉多个空格,并split出所有事件let reg = /\s+/g;//将多个空格去重 \s代表空格event = event.replace(reg," ");let events = event.split(" ");// 循环给所有节点加上事件监听for(let i=0;i<this.length;i++){for(let j=0;j<events.length;j++){events[j] = events[j].toLowerCase();this[i].addEventListener(events[j],func)}}}
3.5实现css方法
- 获取样式封装
- 设置样式封装
- css方法中的回调函数
注意节点,属性,属性值得不同表现形式
// jquery对象上css()方法:有三种方式css("width","200px")设置一个样式 css("width")获取样式 css({"width:100px","height:100px"}})设置多个样式// console.log($("div").eq(0).css("width"));// $("div").eq(1).css("width","200px");$("div").eq(1).css({"background-color":"yellow","height":"200px"});
//css方法:参数有三种方式:一个参数(String,Object),两个参数(直接通过拓展运算符获取所有参数)css(...arg){// 一个参数if(arg.length === 1){if(typeof arg[0] === "string"){//arg[0]代表css()方法的第一个参数即"width"return this.getStyle(this[0],arg[0]);//this代表Jq对象,只有一个参数所以this[0]表示当前DOM节点}else{// 是对象时,循环设置样式// console.log(arg[0]);for(let prop in arg[0]){// console.log(arg[0][prop]);//prop表示属性,arg[0][prop]表示属性值this.setStyle(prop,arg[0][prop]);} }}else{// 两个参数(第一个参数为属性,第二个为属性值)this.setStyle(arg[0],arg[1]);}}setStyle(prop,styleVal){this[0].style[prop] = styleVal;//this[0]表示当前节点}getStyle(ele,style){return getComputedStyle(ele)[style];}
3.6自定义实现$.cssNumber
在myJq.js文件中有以下对象,注意独立于class Jq。
$.cssNumber = {animationIterationCount: true,columnCount: true,fillOpacity: true,flexGrow: true,flexShrink: true,fontWeight: true,gridArea: true,gridColumn: true,gridColumnEnd: true,gridColumnStart: true,gridRow: true,gridRowEnd: true,gridRowStart: true,lineHeight: true,opacity: true,order: true,orphans: true,pm: true,widows: true,zIndex: true,zoom: true
}
Jq.js:在设置style时cssNumber时判断styleVal如果为number型且不在$.cssNumber中时就加px,否则不加px
setStyle(prop,styleVal){// cssNumber时判断styleVal如果为number型且不在$.cssNumber中时就加px,否则不加pxif(typeof styleVal === "number" && !(prop in $.cssNumber)){//注意此处非!的范围 !(prop in $.cssNumberstyleVal = styleVal+"px";}//判断所给的属性是否在$.cssHooks中,在则使用用户扩展的$.cssHooks中的set();// console.log(prop,styleVal);//wh 200pxif(prop in $.cssHooks){$.cssHooks[prop].set(this[0],styleVal);}else{this[0].style[prop] = styleVal;//this[0]表示当前节点}}
myJq.html:使用时$.cssNumber中没有的如width会根据setStyle()中设置的加上px,而opacity在$.cssNumber中所以不会加上px单位。
// $.cssNumber 开闭原则:对外扩展开发,修改关闭console.log($.cssNumber);$("div").eq(1).css("width",200);$("div").eq(1).css("opacity",0.2);// 要自己扩展时,使用$.cssNumber.自定义属性=true即可// $.cssNumber.px = true;
3.7自定义实现$.cssHooks
当用户需要通过css()方法设置或获取某元素信息,但该属性本身不存在,如$("#mydiv1").css("wh")时,就可以通过$.cssHooks 进行扩展。
原有jQuery中$.cssHooks的使用:
// cssHooks()自定义扩展样式$.cssHooks["wh"] = {get(ele){console.log("get()-----");console.log("cssHooks获取后",ele.style["width"]);},set(ele,value){ele.style["width"] = value;ele.style["height"] = value;}};// cssHooks设置:自定义扩展$("#mydiv1").css("wh","200px");// $("#mydiv1").css("wh");
自定义扩展$.cssHooks:
- $.cssHooks本身是一个对象,所以注意设置在class范围外;
- 因为是在css()方法调用时生效,所以在css()方法中,一个参数(String,Object)和两个参数中都需要判断如果参数为在$.cssHooks中时的处理方式即 if(arg[0] in $.cssHooks)。而css方法中都相对调用了setStyle()和getStyle()方法所以,直接在这两个方法中判断即可
// cssHooks设置:自定义扩展$.cssHooks["wh"] = {get(ele){return "width=" + getComputedStyle(ele)["width"] + ";height=" + getComputedStyle(ele)["height"];},set(ele,value){console.log(ele,value);ele.style["width"] = value;ele.style["height"] = value;}}// console.log($("#mydiv1").css("wh"));// console.log($("#mydiv1").css("width"));// $("#mydiv1").css("wh","200px")// $("div").eq(1).css("width","200px");$("div").eq(1).css({"wh":"300px"});
setStyle(prop,styleVal){//判断所给的属性是否在$.cssHooks中,在则使用用户扩展的$.cssHooks中的set();// console.log(prop,styleVal);//wh 200pxif(prop in $.cssHooks){$.cssHooks[prop].set(this[0],styleVal);}else{this[0].style[prop] = styleVal;//this[0]表示当前节点}}getStyle(ele,prop){// console.log(prop,styleVal);//<div id="mydiv1"></div> "wh"//判断所给的属性是否在$.cssHooks中,在则使用用户扩展的$.cssHooks中的get();if(prop in $.cssHooks){return $.cssHooks[prop].get(ele);}else{return getComputedStyle(ele)[prop];}}
4.总结及完整案例
- 类及对象
- 链式调用
- 对象的包装
- 拓展功能
完整示例:
myJq.js:
class Jq{constructor(ele,root){// preObject属性:注意preObject属性表示上一次操作的节点,如果不再有上一次操作的节点就返回document(注意也是Jq对象),使用end()方法获取上一次操作的节点// 通过root标识第一次赋值document。只有第一次root没有赋值时才会为undefined,其他时候都会赋值为nullif(root === "undefined"){this.preObject = new Jq(document,null);}if(root){this.preObject = root;}//$()参数通过typeof可知有三种形式:字符串,函数,NodeList。if(typeof ele === "string"){//如果为string类型,返回获取到的DOM对象(注意获取的可能为多个,所以需要循环)let eles = document.querySelectorAll(ele); this.addDom(eles); }else if(typeof ele === "function"){// 当ele为function时,将其挂载到window的监听事件DOMContentLoaded上window.addEventListener("DOMContentLoaded",ele);}else{// 当参数为原生节点时:如果typeof ele.length为undefined即代表只有一个原生节点,否则类型为Objectif(typeof ele.length === "undefined"){this[0] = ele;this.length = 1;}else{this.addDom(ele); //有多个原生节点 }}}//添加节点addDom(eles){//循环将节点添加到对象上eles.forEach((element,index) => {this[index] = element;});//返回有对象的length属性this.length = eles.length;}//click:注意this.length代表所有的节点数,通过循环this.length即可循环执行所有节点click(func){for(let i=0;i<this.length;i++){this[i].addEventListener("click",func)}}//on():可以绑定多个事件,事件之间通过空格隔开可能存在多个空格需要去重、需要忽略事件的大小写on(event,func){// 通过replace()方法去掉多个空格,并split出所有事件let reg = /\s+/g;//将多个空格去重 \s代表空格event = event.replace(reg," ");let events = event.split(" ");// 循环给所有节点加上事件监听for(let i=0;i<this.length;i++){for(let j=0;j<events.length;j++){events[j] = events[j].toLowerCase();this[i].addEventListener(events[j],func)}}}// eq()即获取对象上的第几个节点,返回的是一个对象eq(index){return new Jq(this[index],this);//此处this代表整个Jq对象}// end()方法:用于获取上一次操作的节点即this.preObjectend(){return this.preObject;}//css方法:参数有三种方式:一个参数(String,Object),两个参数(直接通过拓展运算符获取所有参数)css(...arg){// 一个参数if(arg.length === 1){if(typeof arg[0] === "string"){//arg[0]代表css()方法的第一个参数即"width"return this.getStyle(this[0],arg[0]);//this代表Jq对象,只有一个参数所以this[0]表示当前DOM节点}else{// 是对象时,循环设置样式for(let prop in arg[0]){this.setStyle(prop,arg[0][prop]);//prop表示属性,arg[0][prop]表示属性值}}}else{// 两个参数(第一个参数为属性,第二个为属性值)this.setStyle(arg[0],arg[1]);}}setStyle(prop,styleVal){// cssNumber时判断styleVal如果为number型且不在$.cssNumber中时就加px,否则不加pxif(typeof styleVal === "number" && !(prop in $.cssNumber)){//注意此处非!的范围 !(prop in $.cssNumberstyleVal = styleVal+"px";}//判断所给的属性是否在$.cssHooks中,在则使用用户扩展的$.cssHooks中的set();// console.log(prop,styleVal);//wh 200pxif(prop in $.cssHooks){$.cssHooks[prop].set(this[0],styleVal);}else{this[0].style[prop] = styleVal;//this[0]表示当前节点}}getStyle(ele,prop){// console.log(prop,styleVal);//<div id="mydiv1"></div> "wh"//判断所给的属性是否在$.cssHooks中,在则使用用户扩展的$.cssHooks中的get();if(prop in $.cssHooks){return $.cssHooks[prop].get(ele);}else{return getComputedStyle(ele)[prop];}}}// $.cssNumber
$.cssNumber = {animationIterationCount: true,columnCount: true,fillOpacity: true,flexGrow: true,flexShrink: true,fontWeight: true,gridArea: true,gridColumn: true,gridColumnEnd: true,gridColumnStart: true,gridRow: true,gridRowEnd: true,gridRowStart: true,lineHeight: true,opacity: true,order: true,orphans: true,pm: true,widows: true,zIndex: true,zoom: true
}
// $.cssHooks
$.cssHooks = {};
//返回的是整个Jq对象
function $(ele){return new Jq(ele);//$()函数返还jq对象
}
myJq.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script src="myJq.js"></script><title>myJq</title><style>div {width: 100px;height: 100px; }#mydiv1 {background-color: red;}#mydiv2 {background-color: blue;}</style>
</head>
<body><div id="mydiv1"></div><div id="mydiv2"></div><script>// console.log($("div"));// 传入参数为原生节点:参数个数可为一个/多个/数组// console.log($(document.querySelectorAll("div")));//NodeList(2) [div#mydiv1, div#mydiv2]// $(function(){// console.log("函数");// });// click// $("div").click(function(){// console.log("click div");// });// on// $("div").on("mouseOver moUseout",function(){// console.log("执行on");// });//end// console.log($("div").eq(1).end());//Jq {0: div#mydiv1, 1: div#mydiv2, length: 2}// jquery对象上css()方法:有三种方式css("width","200px")设置一个样式 css("width")获取样式 css({"width:100px","height:100px"}})设置多个样式// console.log($("div").eq(0).css("width"));// $("div").eq(1).css("width","200px");// $("div").eq(1).css({"background-color":"yellow","height":"200px"});// $.cssNumber 开闭原则:对外扩展开发,修改关闭console.log($.cssNumber);$("div").eq(1).css("width",200);$("div").eq(1).css("opacity",0.2);// 要自己扩展时,使用$.cssNumber.自定义属性=true即可// $.cssNumber.px = true;// cssHooks设置:自定义扩展$.cssHooks["wh"] = {get(ele){return "width=" + getComputedStyle(ele)["width"] + ";height=" + getComputedStyle(ele)["height"];},set(ele,value){console.log(ele,value);ele.style["width"] = value;ele.style["height"] = value;}}// console.log($("#mydiv1").css("wh"));// console.log($("#mydiv1").css("width"));// $("#mydiv1").css("wh","200px")// $("div").eq(1).css("width","200px");// $("div").eq(1).css({"wh":"300px"});</script>
</body>
</html>