先宣传一波自己的博客系统,地址: http://www.97blognb.cn/#/
技术实现vue + element + 自己的一个UI库 + node + express + mysql + pm2 + nginx
系统分为三个端:前端, 管理端, 服务端
有想法一起交流的可以加我联系方式, 博客里有
1.实现目标
我们要封装的是一个类似于jquery的简易框架,实现的功能有获取节点,包括通过id获取节点,类名获取节点,后台获取节点,子代获取节点,标签获取节点,总之是符合大多数css选择器的写法都能获取节点,以及创建节点,获取文本框的值,获取dom的文本节点,设置样式,设置属性,以及添加类名,移除类名等一系列基本功能
2.实现方法及jquery的思想
前几年,一套jquery框架风靡前端界,那是找工作只要说熟练个jquery基本上进去没问题,确实,jquery封装的东西能够解决我们日常的前端开发工作以及繁琐的节点操作以及大多数兼容性问题,不得不说,jquery是一个很好的框架,随后随着node的兴起,越来越多的框架慢慢融入到主流中,jquery的势头才慢慢消退,不过直到现在,如react,vue,angular虽然占据着主流,但是jquery的影响力丝毫不比这些框架差,依然还是有很多企业在用着jquery。
jquery曾经这么火,也这么好用,自然少不了一些大牛们去研究他的思想以及源码,作为一个前端小白,楼主也是非常好奇,在网上看了一些关于jquery的介绍以及思想,然后自己动手,封住了一个非常简单的类似于jquery的小小的框架
好了,现在先让我们看下jquery是怎么封装的
jQuery是一个快速,小巧,功能丰富的JavaScript库。它通过易于使用的API在大量浏览器中运行,使得HTML文档遍历和操作,事件处理,动画和Ajax更加简单。通过多功能性和可扩展性的结合,jQuery改变了数百万人编写JavaScript的方式 --摘自官网
使用jQuery的第一步,往往就是将一个选择表达式,放进构造函数jQuery()(简写为$),然后得到被选中的元素。
看到这里,将一个选择表达式,放进构造函数中,然后给这个构造函数添加原型方法,调用,这样就是实现了封装
3.代码
第一步我们先定义一个构造函数
function le(ele,index){
return le.prototype.gele(ele,index);
}
上面这个构造函数接收两个参数,第一个为查询的dom元素,第二个为索引,当要查询的dom有多个时,会根据这个索引来查找具体的元素
至于里面的return语句,是直接调用原型中的gele方法,然后在gele方法里返回这个this,这样就形成了可以链式操作,在这里,我并没有去new一个实例,然后通过实例去调用这个原型方法,因为只是要实现效果,所以就是直接用通过原型去调用
第二部我们给le的原型添加方法
le.prototype={
"constructor":le,
gele:function(ele,count){
this.ele=[];
this.gettri=""
if(count!==undefined){
this.ele=document.querySelectorAll(ele)[count];
}
else{
this.ele=document.querySelectorAll(ele);
}
return this;
}
}
上面gele方法就是刚开始调用le方法时在return语句里调用的方法,这个方法的作用就是找到你所要操作的节点,第一个为字符串,第二个为下标值,最后在这个方法的最后将这个原型对象返回出来吗,就形成了链式调用
HTML里调用
console.log(le("#div3"))
我们看下实现的效果图
因为返回的是当前this,所以打印出的就是这个原型里的所有属性和方法,得到的是一个le原型的对象,那怎么得到dom对象呢,下面就会提到,这点不像jq,但是由于我们不做过深的研究,所以暂时不考虑,等以后有时间了,会修复这个问题 ,当然如果看到这里的小伙伴会想,那我操作dom的时候是不是也都要.ele来操作,告诉你,不需要,用法跟jq用法是一样的,当然也会想,既然这么繁琐,何不直接用原生来操作,答案也是一样的,后面用的话会跟jq是一样的用法,不需要.ele来操作,关于这个问题,我后续会修复
再来看上面的定义的ele数组,他是将所查询到的dom保存在ele数组中,所以这时我们只要这样操作
console.log(le("#div3").ele)
然后看下效果
得到了一个匹配到的dom数组,这个就是我们得到dom对象了
当然这是一个数组,我们可以通过传入索引值来得到具体的dom元素
console.log(le("#div3",0).ele)
然后看下效果图
这样就得到了具体的dom元素
好了,能够得到dom元素,接下来的就好办了,我们后面继续添加方法
le.prototype={
"constructor":le,
gele:function(ele,count){
this.ele=[];
this.gettri=""
if(count!==undefined){
this.ele=document.querySelectorAll(ele)[count];
}
else{
this.ele=document.querySelectorAll(ele);
}
return this;
},
cele:function(ele,str=""){
let el=document.createElement(ele);
if(str!==""){
el.appendChild(document.createTextNode(str));
}
this.ele[0].appendChild(el);
return this;
},
val:function(val=""){
if(val===""){
return this.ele[0].value;
}
else{
this.ele[0].value=val;
return this;
}
},
html:function(val=""){
if(val===""){
return this.ele[0].innerHTML;
}
else{
this.ele[0].innerHTML=val;
}
},
css:function(obj,val){
for(let j=0;j<this.ele.length;j++){
if(typeof obj==='object'){
for(let i in obj){
this.ele[j].style[i]=obj[i];
}
}
else{
this.ele[j].style[obj]=val;
}
}
return this;
},
seter:function(obj,val){
if(typeof obj==='object'){
for(let i in obj){
if(i==='class'){
this.geter(i);
this.ele[0].setAttribute(i,this.gettri ? this.gettri+" "+obj[i] : obj[i]);
}
else{
this.ele[0].setAttribute(i,obj[i]);
}
}
return this;
}
else{
if(obj==='class'){
this.ele[0].className ? this.ele[0].className+=" "+val : this.ele[0].className=val;
return this;
}
this.ele[0].setAttribute(obj,val);
return this;
}
},
geter:function(val){
this.gettri=this.ele[0].getAttribute(val);
return this;
},
removeer:function(obj){
if(typeof obj==='object'){
for(let i in obj){
this.ele[0].removeAttribute(obj[i]);
}
}
else{
this.ele[0].removeAttribute(obj);
}
return this;
},
removeClass:function(classor){
this.ele[0].classList.remove(classor);
return this;
},
//为IE浏览器下操作
optionIe:function(str){
let node=document.getElementsByTagName("*");
let dcu;
for(let i=0;i<node.length;i++){
if(this.subStr(str).indexOf('.')){
if(' '+node[i].className+' '.indexOf(' '+ele.substring(1,ele.length)+' ')>-1){
dcu.push(node[i]);
}
}
}
return dcu;
},
// 字符串分割
subStr:function(str,start=0,end=1){
return str.substring(start,end);
}
},
以上为le原型对象添加了多个添加样式,添加类,添加属性,移除类,得到属性,得到文本值,设置文本值,创建节点等方法,用法跟jquery用法一样
我们接下来简单看几个例子
我们最初的div 是这个样子的
然后我们给它设置10px的圆角
le("#div3").css("border-radius","10px")
然后看下效果,样式成功设置
看到这里,会有有一个想法,那如果想要设置多个样式呢,那我们只需要传入一个json就可以了
比如我想要这个div圆角,而且边框又变成黑色边框,可以这样
le("#div3").css({"border-radius":"10px","border-color":"#000"})
是不是跟jq的css()方法差不多
到这里还不能满足需求,如果有这么一个场景,就是class=“wrap”的div有多个,我想要同时设置样式,原生的写法,循环遍历,但是这里不用,比如像下面这样,有两个同为class="wrap"的div
给他们设置初始的样式,
然后我想同时给它们设置黑色边框和圆角,这样写就可以了
le(".wrap").css({"border-radius":"10px","border-color":"#000"})
这样写就是给所有class="wrap"的div设置了样式,看下效果
我既想添加样式又想给div设置内容
le("#div3").css({"border-radius":"10px","border-color":"#000"}).html("1111")
效果图
后面的方法就不在一一写出了,其余的几个方法跟jq用法是一样的
但是我们做到这里并没有结束,当然在这个小小框架中,没有任何影响,但是在一些大项目大公司里,模块化开发是非常有必要的,有时候一个项目多人开发,这时候我们在这样写,就会造成全局变量污染,对整个开发团队和后面的人是非常不友好的,所以,在这里,我们需要做一点小小的改动
写一个匿名函数,让它自执行,然后将这些代码写在自执行函数中,这样,就不会造成变量污染,而且提高了性能,因为它在执行一次就会销毁掉
(function(window){
function le(ele,index){
return le.prototype.gele(ele,index);
}
le.prototype={
...
}
window.le=le;
})(window);
上面的代码就是将代码写在了自执行函数中,然后通过window.le将属性暴露给外界,外界就可以像var定义一样使用这个属性了,我们将等式右边的le替换一下,替换成$,形式上就会想jq一样了。
有感兴趣的小伙伴可以复制最后面的源码,玩一下
4.结束语
我们只是简单的封装了一下,对于jq这种强大的框架还有很多要学习和借鉴之处,学习它的核心思想,设计思想,以及代码的封装性,如果实现这个功能的,当然对于这个项目,依然有一些东西没有考虑到,其中有一点就是前面所讲的需要通过le(str).ele才能打印出dom 对象,这是一个严重的不足,我吧它归列到了bug里,等待后续有时间,我会修复这个问题
(function(window){
function le(ele,index){
return le.prototype.gele(ele,index);
}
le.prototype={
"constructor":le,
gele:function(ele,count){
this.ele=[];
this.gettri=""
if(count!==undefined){
this.ele=document.querySelectorAll(ele)[count];
}
else{
this.ele=document.querySelectorAll(ele);
}
// this.wjs.push=this.ele;
return this;
},
cele:function(ele,str=""){
let el=document.createElement(ele);
if(str!==""){
el.appendChild(document.createTextNode(str));
}
this.ele[0].appendChild(el);
return this;
},
val:function(val=""){
if(val===""){
return this.ele[0].value;
}
else{
this.ele[0].value=val;
return this;
}
},
html:function(val=""){
if(val===""){
return this.ele[0].innerHTML;
}
else{
this.ele[0].innerHTML=val;
}
},
css:function(obj,val){
for(let j=0;j<this.ele.length;j++){
if(typeof obj==='object'){
for(let i in obj){
this.ele[j].style[i]=obj[i];
}
}
else{
this.ele[j].style[obj]=val;
}
}
return this;
},
seter:function(obj,val){
if(typeof obj==='object'){
for(let i in obj){
if(i==='class'){
this.geter(i);
this.ele[0].setAttribute(i,this.gettri ? this.gettri+" "+obj[i] : obj[i]);
}
else{
this.ele[0].setAttribute(i,obj[i]);
}
}
return this;
}
else{
if(obj==='class'){
this.ele[0].className ? this.ele[0].className+=" "+val : this.ele[0].className=val;
return this;
}
this.ele[0].setAttribute(obj,val);
return this;
}
},
geter:function(val){
this.gettri=this.ele[0].getAttribute(val);
return this;
},
removeer:function(obj){
if(typeof obj==='object'){
for(let i in obj){
this.ele[0].removeAttribute(obj[i]);
}
}
else{
this.ele[0].removeAttribute(obj);
}
return this;
},
removeClass:function(classor){
this.ele[0].classList.remove(classor);
return this;
},
// removeChilder:function(ele){
// this.ele[0].removeChild(this.ele[0].children());
// return this;
// },
animate:function(obj){
},
//为IE浏览器下操作
optionIe:function(str){
let node=document.getElementsByTagName("*");
let dcu;
for(let i=0;i<node.length;i++){
if(this.subStr(str).indexOf('.')){
if(' '+node[i].className+' '.indexOf(' '+ele.substring(1,ele.length)+' ')>-1){
dcu.push(node[i]);
}
}
}
return dcu;
},
// 字符串分割
subStr:function(str,start=0,end=1){
return str.substring(start,end);
}
},
window.le=le;
})(window);