温馨提示

本文阅读对象: 对 JavaScript 有一定的了解,如果你没有学过或者忘记 JavaScript 某些操作,请看 阮一峰 JavaScript 教程 。

导语

DOM 有许多 API ,但是有些 API 太难用了。
比如,想获取某 element 的所有兄弟该怎么办?
再比如,我想给某 element 加多个 class ,你说有 node.classList.add( )啊。那我想加 100 个怎么办?总不能写 100 遍吧,so 自己封装个 API 。

向大佬学习

自己写一个毫无头绪啊,那jQuery是怎么做的,我们模仿一下嘛。 在jQuery这个库里,有许多函数,比如addClass( ),.css( ),.data( )等等。也就是说,jQuery是一个大仓库,仓库里有很多工具,我们需要什么的时候就用什么工具。 现在我们要做的就是,自己生产很多工具,然后建立自己的仓库,我们想用什么就从仓库里拿出来什么。

1. 生产工具

假设我们现在需要两个工具,分别是获取兄弟 element 和 添加多个 class 。

实现功能

1. 获取兄弟 element

操作步骤:

  1. 在 html 中有一个 ul 标签,在 ul 中有 5 个 li 。
<ul><li id="item1">item1</li><li id="item2">item2</li><li id="item3">item3</li><li id="item4">item4</li><li id="item5">item5</li>
</ul>
复制代码
  1. 获取 id 为 item3 的兄弟元素。
    首先定义一个 allChildren 变量来存储 item3 的父节点所有的子元素。 获取子元素 DOM 有两个 API ,node.parent.children 和 node.parent.childNodes 。应该选择哪个呢?

    parent.childNodes:获取节点,不同浏览器表现不同;

  IE:只获取元素节点;

  非IE:获取元素节点与文本节点;

  parent.children:获取元素节点,浏览器表现相同。

  因此建议使用children。   

var allChildren = item3.parentNode.children;
复制代码

上图是在某博客页面做的测试,可以看到使用 parent.childNodes 获取到了 text 节点。

  1. 定义一个空数组来存兄弟元素,此时数组长度为0。
var arr = {length:0};
复制代码
  1. 遍历所有的孩子节点,如果不是 item3 ,那么就存到 arr 数组中。
for(var i = 0;i < allChildren.length;i++){if(allChildren[i] !== item3){arr[arr.length] = allChildren[i];arr.length++;}
}
复制代码

小技巧:使用 arr[arr.length] = allChildren[i]; 使得数组下标依次存储 item 元素。

完整代码

<ul><li id="item1">item1</li><li id="item2">item2</li><li id="item3">item3</li><li id="item4">item4</li><li id="item5">item5</li>
</ul>var allChildren = item3.parentNode.children;
var arr = {length:0};for(var i = 0;i < allChildren.length;i++){if(allChildren[i] !== item3){arr[arr.length] = allChildren[i];arr.length++;}
}console.log(arr);
复制代码

运行结果

注意: 这个 arr 数组是一个伪数组,它的原型链直接指向了 Object 并没有指向 Array.prototype ,只有原型链中指向 Array.prototype 的数组才是真正的数组。如果原型链中不包含 Array.prototype 是没有数组.push( )等方法的。

封装成函数

  1. 包装一下,加个 function ,同时起个名字,方便调用。
function getSiblings(){var allChildren = item3.parentNode.children;var arr = {length:0};for(var i = 0;i < allChildren.length;i++){if(allChildren[i] !== item3){arr[arr.length] = allChildren[i];arr.length++;}}console.log(arr);
}
复制代码
  1. 我们给这个函数一个返回值,返回值呢就是这个数组,把 console.log(arr) 改成 return arr
  2. 此时我们发现,item3 这个 id 是函数外面的值,是在调用函数的时候传参才能获取到,所以给我们的函数加一个参数,同时把 item3 改成参数 node 。
function getSiblings(node){var allChildren = node.parentNode.children;var arr = {length:0};for(var i = 0;i < allChildren.length;i++){if(allChildren[i] !== node){arr[arr.length] = allChildren[i];arr.length++;}}return arr;
}
console.log(getSiblings(item3));
复制代码

2. 添加多个 class

操作流程同上,这里只给出代码和必要的解释。

实现功能

<ul><li id="item1">item1</li><li id="item2">item2</li><li id="item3">item3</li><li id="item4">item4</li><li id="item5">item5</li>
</ul>var classes = {'a':true,'b':false,'c':true}
for(var key in classes){var value = classes[key];if(value){item3.classList.add(key);}else{item3.classList.remove(key);}
}
复制代码

利用 hash 存储是否添加的 class ,如果为 true ,添加给 item3 ,如果为 false 移除该 class 。

封装成函数

function addClasses(node,classes){for(var key in classes){var value = classes[key];if(value){node.classList.add(key);}else{node.classList.remove(key);}}
}
addClasses(item3,{'a':true,'b':false,'c':true});
// 传入参数包括节点和要添加的 class
复制代码

优化整理

function addClasses(node,classes){for(var key in classes){var value = classes[key];var methodName = value ? 'add':'remove';node.classList[methodName](key);}
}
addClasses(item3,{'a':true,'b':false,'c':true});
复制代码

这里要说明一下,我们根据传来的 key 值为 true 或者 false 来决定是否添加这个 class。node.classList.add(key);node.classList.remove(key); 是属于一类代码,区别就是调用的是 add 方法还是 remove 方法,那么存在优化的可能性。
定义一个 methodName 变量存储 value 值,也就是遍历过程中每次的 true 或者 false。让 node.classList[methodName](key) 每次直接调用 methodName 即可完成操作。
备注:如果你只了解对象 obj.add( ) 这种调用,不理解关于obj[]( ) 调用方法,情回顾 JavaScript 基础。

运行结果

2. 建造仓库放工具

现在我们的工具都造好了,就要给工具造房子了。
jQuery 工具的房子叫 jQuery ,那我们也取一个属于自己的名字,比如我的叫 simpleTools 。

window.simpleTools = function(){ return{getSiblings:function(){},addClass:function(){}};
};
复制代码

window.simpleTools 是我们的大房子,这是一个函数大房子,房子里面现在住着 getSiblings 对象和 addClass 对象,这就好比是放工具的架子。接下来,我们把上面封装好的函数放在对应的架子上。

window.simpleTools = function(node){return{getSiblings:function(){var allChildren = node.parentNode.children;var arr = {length:0};for(var i = 0;i < allChildren.length;i++){if(allChildren[i] !== node){arr[arr.length] = allChildren[i];arr.length++;}}return arr;},addClass:function(classes){for(var key in classes){var value = classes[key];var methodName = value ? 'add':'remove';node.classList[methodName](key);}}};
};
复制代码

传给 function 的 node 就好像是我们的仓库小管家,一旦他被通知要工作了(有参数传过来),那他就去告诉每一个工具,做好准备随时准备开工。

现在加几句测试语句,看看运行结果。

var nodeTest = simpleTools(item3);
console.log(nodeTest.getSiblings());
nodeTest.addClass({'a':true,'b':false,'c':true});
复制代码

运行结果

很好,和之前的运行结果相同,说明我们并没有因为放到仓库里而产生bug。

小结

到这为止,你已经学会了写一个自己的仓库。我们再来回顾一下流程吧。

生产工具:

  1. 实现功能
  2. 封装成函数
  3. 适当优化

建造仓库放工具:

  1. 建一个仓库
  2. 放入工具

快动手实现一个属于自己的仓库吧, 代码的后续优化请看 从封装函数到实现简易版自用jQuery (二)。

从封装函数到实现简易版自用jQuery (一)相关推荐

  1. 字符串拼接函数的实现(简易版)

    字符串操作函数的实现----字符串拼接 1 #include <stdio.h> 2 3 // 求字符串长度 4 int getLength(char *str) { 5 6 int le ...

  2. 5 拦截器拦截请求路由_手写简易版axios拦截器,实现微信小程序wx.request的封装与拦截...

    前言: axios是一个功能强大的网络请求库,其中拦截器又是axios的精髓.在小程序的开发或者需要手动实现ajax的时候,没有实现对请求的拦截,开发的时候非常不方便,因此手写一个简易版的axios拦 ...

  3. 从零手写实现简易版MMKV(一)

    概述 MMKV是支持多平台的高性能键值对持久化存储组件,其核心原理是利用mmap内存映射文件,关于它的详细介绍和更多原理参看MMKV开源git地址. 从零开始手写(其实是抄写-.-!)简易版MMKV( ...

  4. C与C++游戏项目练习9:接金币游戏简易版

    ## C与C++游戏项目练习9:接金币游戏简易版 还是只能在devC++里面运行,不要用VS!!! 还是只能在devC++里面运行,不要用VS!!! 还是只能在devC++里面运行,不要用VS!!!* ...

  5. 利用python实现简易版的贪吃蛇游戏(面向python小白)

    前言 这篇文章主要给大家介绍了关于如何利用python实现简易版的贪吃蛇游戏的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学 ...

  6. python实现简易版成绩管理系统_Python 实现简易版成绩管理系统!

    上一次,公众号介绍了如何使用 Python 实现单链表,下面让我们一探单链表的简单应用:在命令行,实现简易版成绩管理系统. 这次的简易版成绩管理系统,主要有六大功能:增加学生信息.删除学生信息.修改学 ...

  7. 肝一波 ~ 手写一个简易版的Mybatis,带你深入领略它的魅力!

    零.准备工作 <dependencies><dependency><groupId>mysql</groupId><artifactId>m ...

  8. python简易版成绩管理系统_Python 实现简易版成绩管理系统

    上一次,公众号介绍了如何使用 Python 实现单链表,下面让我们一探单链表的简单应用:在命令行,实现简易版成绩管理系统. 这次的简易版成绩管理系统,主要有六大功能:增加学生信息.删除学生信息.修改学 ...

  9. js计算器代码加减乘除_如何用jQuery做一个简易版计算器

    经过几个小时地敲敲打打,终于把这一部分的代码完成了. 这类问题的难点不在于布局,而是如何恰当地使用jQuery达成计算.交互的目的,比如储存.显示数值,数据类型的转换等. 在听老师讲课之前的我,思路是 ...

最新文章

  1. HTML的相关路径与绝对路径的问题---通过网络搜索整理
  2. 前端学习(2924):watch的用法
  3. docker web程序本地化_Docker-01-Docker介绍
  4. 动态生成类_springboot动态生成类属性
  5. 【clickhouse】clickhouse 表引擎之 Merge
  6. shell 构建脚本基础
  7. 苹果手机上网很慢_手机信号满格,但上网速度却很慢?来听听通信专家怎么说的...
  8. python 安装whl文件
  9. 【算法随记二】线卷积积分及其在图像增强和特效方面的应用(一)
  10. GO分析和KEGG分析都是啥?
  11. 电脑通过 adb 控制安卓手机(1台或多台)自动加微信
  12. 比较Cint() , int() , fix() ,round()的区别
  13. 使用BeanEditForm来创建用户表单
  14. Java实现发电子邮件,快去给你好基友发一封邮件~
  15. 十八、商城 - 规格管理-模板管理(6)
  16. MIPS指令集和汇编
  17. SQL Server Management Studio(SSMS)无法连接到服务器,及解决方案
  18. Verilog通过锁相环实现倍频,分频,相位偏移
  19. 全连接层介绍以及简单实现
  20. 单片机与STM32问题及解决

热门文章

  1. sudo执行脚本找不到环境变量解决方法
  2. [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)...
  3. ExtJs自学教程(1):一切从API開始
  4. 领悟Web设计模式(转)
  5. iOS开发笔记(十七):持久化方案之 NSUserDefaults
  6. 哪种云存储服务最适合你?
  7. 极客Web前端开发资源大荟萃
  8. [Share]10 Free EBooks for Web Designers
  9. 计算路由汇总的方法(CIDR)
  10. 【译】需要学习的是编程,而不是编程语言