模拟jQuery框架做一些简单的封装

什么是封装?

打包?安装?组合?

我们将一些功能组装在一起,生成一些简单好用的API。复杂的API就不用在看了。

比如:我们通过元素的ID获取页面的一个元素DOM对象

原生的API:

document.getElementById(id)

我们封装之后的API:

// 封装
function $(id){
   return docuement.getElementById(id);
}
// 使用
var x = $(id);

jQuery库,里面存在大量的JavaScript函数

获取jQuery  

①//cdn 引入

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.js"></script>

②下载之后在项目导入

公式:$(selector).action() //选择器就是css的选择器,css中的选择器它全部能用!

案例:

<a href="#" id="test-jquery">点击</a>

$("#test-jquery").click(function(){

alert('hello,jQuery!');

})

文档工具站:https://jquery.cuishifeng.cn/index.html

案例:

//获取鼠标当前的坐标

mouse:<span id="mouseMove"></span>

<div id="divMove">移动鼠标</div>

//当网页元素加载完毕之后,响应事件

$(function(){

$('#divMove').mousemove(function (e){

$('#mouseMove').text('x:'+e.pageX+'y:'+e.pageY)

})

});

//操作dom

<ul id="test-ul">

<li class="js">JavaScript</li>

<li name="python">Python</li>

</ul>

$('test-ul li[name=python]').text();//获得值

$('test-ul li[name=python]').text('123456');//设置值

//CSS的操作

$('test-ul li[name=python]').css({"color","red"});

//元素的显示和隐藏 本质是display:none;

$('test-ul li[name=python]').show();  $('test-ul li[name=python]').hide();

[1]获取页面元素的DOM对象

常用的API:

  • getElementById()

  • getElementsByTageName();

  • getElementsByClassName();

  • querySelector()

  • querySelectorAll()

我们自己实现一个方法,命名为 $(),实现上面的所有的API的使用:

/**
* 获取页面元素的DOM对象的方法\
* @param selector
*         1 #xxxx 表示是通过id查找元素,我直接返回一个对象   $("#box")
*         2 .xxxxx 通过class名字获取一组元素
*         3   其他的情况 标签   ".abc li"
*/
function $(selector){
   if(!selector){
       return document;// 如果没有指定selector,就返回document对象本身
  }
   // 判断是否是id选择器
   if(selector.indexOf("#")==0){
       return document.getElementById(selector.substr(1)); // #box
  }
   // 判断是否是类样式名称选择器 ".abc"
   if(selector.indexOf(".") == 0 && selector.indexOf(" ") == -1 && selector.indexOf(",") == -1){
       return document.getElementsByClassName(selector.substr(1));
  }
   // 其他选择器
   var elements = document.querySelectorAll(selector);
   if(elements.length == 1){
       return elements[0];// 如果只有一个就返回一个,
  }
   return elements;// 如果有多个就返回集合
   return undefined; // 返回原生的DOM对象数组
}

调用测试:

    <script>
       window.onload=function(){
           console.log($("#username"));
           console.log($(".abc"));
           console.log($("li"));
           console.log($("ul .def"))
      }
   </script>
   <input type="text" id="username">
   <ul>
       <li class="abc"></li>
       <li class="abc"></li>
       <li class="abc"></li>
       <li class="abc"></li>
       <li class="def"></li>
       <li class="def"></li>
       <li class="def"></li>
   </ul>
   <input type="text" class="def">

[2]操作这些元素的属性

原生的API:

var attValue = xxxDom.attrName ;
xxxDom.attrName = attrValue;

我们自己封装的方法:

方法1:常规属性操作

/**
* 操作DOM对象的属性
* @param {} dom 要处理的DOM对象
* @param {*} attrName
*       1 string类型,表示是属性的名字 checked
*       2 object类型, 我们认识是要设置一组属性 {size:100,name:卡卡西}
* @param {*} attrValue 可选的参数,如果没有传递,就是获取属性值,如果传递了就是设置属性值        
*      
*/
function $attr(dom,attrName,attrValue){
   if(!dom || !attrName){
       return; //如果DOM不存在,直接返回
  }
   // 判断第二个参数是什么类型
   if((typeof attrName)== 'string'){  // attrName = "value"
       // 判断是否有第三个参数,如果有就是设置属性值,如果没有就是获取属性值
       if(attrValue){
           dom.setAttribute(attrName,attrValue);
      }else{
           return dom.getAttribute(attrName);
      }
       return;
  }else if((typeof attrName) == 'object'){  //attrName = {value:"漩涡鸣人",size:20,type:"button"}
       var obj = attrName;
       // 要同时设置多个属性值
       for(var aname in obj){// 可以通过for循环取出对象的属性和value
           dom.setAttribute(aname,obj[aname]);
      }
       return;
  }
   // 没有其他情况 报错
   throw "参数有误:"+attrName;
}

使用方法

    <input type="text" id="uname" value="宇智波佐助">
   <script>
       var unameDom = $("#uname");
       // 获取value的值
       var value = $attr(unameDom,"value");
       console.log("通过$attr获取的属性值:"+value);
       // 设置value的值
       $attr(unameDom,"value","旗木卡卡西");
       // 设置多个属性值
       $attr(unameDom,{value:"漩涡鸣人",size:20,type:"button"})
       // 错误的调用方法
       // $attr(unameDom,123)
   </script>

特殊的属性操作: innerHTML,innerText, class

/**
* 给一个或者多个元素...
* 如果className存在就删除 如果不存在就添加
* @param {*} obj
* @param {*} className
*/
function $switchClass(obj,className){
   if(!obj || !className){
       return;
  }
   // 判断是一个还是一组?
   if(obj.length && obj.length > 1){
       //集合
       for(var x = 0;x < obj.length;x ++){
           if( obj[x].className.indexOf(className)!=-1){// 类名本身存在
               $removeClass( obj[x],className);
          }else{
               $addClass( obj[x],className);
          }
      }
  }else{
       if(obj.className.indexOf(className)!=-1){// 类名本身存在
           $removeClass(obj,className);
      }else{
           $addClass(obj,className);
      }
  }
}
/**
* 给一个或者多个元素删除指定的类名
* @param {*} obj
* @param {*} className
*/
function $removeClass(obj,className){
   if(!obj || !className){
       return;
  }
   // 判断是一个还是一组?
   if(obj.length && obj.length > 1){
       //集合
       for(var x = 0;x < obj.length;x ++){
           obj[x].className = obj[x].className.replaceAll(className,"");
      }
  }else{
       obj.className = obj.className.replaceAll(className,"");
  }
}
/**
* 给一个或者多个元素添加指定的类名
* @param {*} obj 可以是一个对象,也可以是一组对象
* @param {*} className
*/
function $addClass(obj,className){
   if(!obj || !className){
       return;
  }
   // 判断是一个还是一组?
   if(obj.length && obj.length > 1){
       //集合
       for(var x = 0;x < obj.length;x ++){
           obj[x].className = (obj[x].className?obj[x].className:"") + " " + className;
      }
  }else{
       obj.className = (obj.className?obj.className:"") + " " + className;
  }
}

测试:

        // class的测试
       $addClass($("li"),"st");
       $removeClass($("li"),"abc");
       $switchClass($("li"),"def");

[3]操作这些元素的CSS

封装的方法:

/**
* 给元素设置或者获取样式
* @param {*} dom 要操作的一个或者一组对象
* @param {*} op
*         1 string 操作一个样式属性
*         2 设置一组样式属性
* @param {*} cssValue
*       可选的,如果传递了,就是设置一个样式属性,如果没有传递就是获取样式属性值
*/
function $css(dom,op,cssValue){ // background-color
   if(!dom || !op){
       return; //第一个和第二个参数必须有
  }
   // 判断第二个参数是什么
   if((typeof op) == 'string'){
       //单个属性操作
       if(dom.length){
           // 一组DOM
           if(!cssValue){
               throw "无法同时获取多个元素的样式"+op+"的值";
               return;
          }
           // 遍历处理 这里只能是设置属性
           for(var x = 0;x < dom.length;x ++){
               dom[x].style[$changeCssName(op)] = cssValue;
          }
      }else{
           // 单个dom
           if(cssValue){// 设置单个DOM的样式属性
               dom.style[$changeCssName(op)] = cssValue;
          }else{
               // 获取单个DOM的值
               var value = dom.style[$changeCssName(op)];
               if(isNaN(parseInt(value))){
                   return value;
              }else{
                   return parseInt(value);
              }
          }
      }
       return;
  }else if((typeof op) == 'object'){// {"width":"50px","height":"50px","background-color":"red"}
       // 多个样式设置
       if(dom.length){
           // 多个DOM,多个样式
           for(var x = 0;x < dom.length;x ++){// 遍历DOM集合
               // 遍历属性对象
               for(var attrName in op){
                   dom[x].style[$changeCssName(attrName)] = op[attrName]
              }
          }
      }else{
           // 一个DOM,多个样式
            // 遍历属性对象
           for(var attrName in op){
               dom.style[$changeCssName(attrName)] = op[attrName]
          }
      }
       return;
  }
   throw "参数有误:"+op;
}
/**
* 将 css中的属性的名称转换为JS中的样式属性的名称
*   border-color -> borderColor;
* @param {*} cname  
*/
function $changeCssName(cname){  
   // border-top-color
   if(cname.indexOf("-") == -1){
       // 这个属性的名字就是一个单词,不需要转换
       return cname;
  }
   // 这里就需要转换了
   var names = cname.split("-"); //border-top-color [border,top,color]
   var name = names[0]; // borderTop
   for(var x = 1;x < names.length;x ++){
       var n = names[x] // top
       name += n.charAt(0).toUpperCase() + n.substr(1);
  }
   return name;
}

测试:

    <div class="cssTest" id="divBox"></div>
   <div class="cssTest"></div>
   <script>
       // 设置多个样式属性
       $css($(".cssTest"),{"width":"100px","height":"100px","border-style":"solid","border-width":"1px"})
       // 给单个元素设置样式
       $css($("#divBox"),"background-color","#ccc")
       // 获取属性
       var w = $css($("#divBox"),"width");
       console.log("宽度:"+w);
       console.log($css($("#divBox"),"backgroundColor"));
       // 错误的使用方式
       console.log($css($("li"),"backgroundColor"));
   </script>

[4]操作元素的内容

[5]操作元素的事件

原生的api:

XXDom.onxxxx = function(){}
xxDom.addEventListener("xxxx",function);

我们的封装:

/**
* 解绑事件
* @param {*} dom 一个或者一组元素
* @param {*} eventName 事件的名称
* @param fn 函数本身
*/
function unbind(dom,eventName,fn){
   if(!dom || !eventName || !fn){
       return; //三个参数必须齐全
  }
   dom.removeEventListener(eventName,fn);
}
/**
* 绑定事件
* @param {*} dom 一个或者一组dom元素
* @paran {} eventName 事件的名称
* @param {*} fn 一个事件
*/
function bind(dom,eventName,fn){
   if(!dom || !eventName || !fn){
       return; //三个参数必须齐全
  }
   dom.addEventListener(eventName,fn);
};