DOM操作和样式操作库的封装
一、DOM常用方法和属性复习
以下粗略的罗列一下DOM的常用方法和属性,由于不是介绍DOM的基础内容,所以就不一一详细说明各个方法和属性了(学习DOM的封装的,一般都对基础DOM比较熟悉了)。
1.1、获取页面中元素的方法
document.getElementById()
context.getElementsByTagName():把指定容器中子孙容器中所有标签名为TAGNAME的都获取到了
document.getElementsByName():在IE浏览器中只对表单元素的name起作用
document.body || document.documentElement
context.querySelector()、context.querySelectorAll():在IE6~8下不兼容,通过这个获取到的节点集合,不存在DOM映射
1.2、描述节点和节点之间关系的属性
childNodes
children:在IE6~8下获取的结果和标准浏览器获取的结果不一致
parentNode
previousSibling/previousElementSibling
nextSibling/nextElementSibling
lastChild/lastElementChild
firstChild/firstElementChild
注意:
在标准浏览器中会把空格和换行当做文本节点处理
所有带Element的属性在IE6~8中都不兼容
1.3、DOM的增删改
createElement
document.createDocumentFragment()
appendChild
insertBefore
cloneNode(true/false)
replaceChild
removeChild
get/set/removeAttribute
二、获取元素的方法封装
2.1、获取某一个容器的元素子节点(children)
首先获取所有的子节点(childNodes),在所有的子节点中,把元素节点过滤出来(nodeType===1的时候代表的是元素节点)。
如果该方法传递两个参数,第二个参数是一个标签名的话,我们还要在获取的子元素中把对应标签名进行二次筛选。
function children(curEle, tagName) {
var ary = [];
// --> IE6~8不能使用内置的children属性,我们自己写代码实现
if(/MSIE (6|7|8)/i.test(navigator.userAgent)) {
var nodeList = curEle.childNodes;
for (var i = 0, len = nodeList.length; i < len; i++){
var curNode = nodeList[i];
if (curNode.nodeType === 1) {
ary[ary.length] = curNode;
}
}
nodeList = null;
} else {
// 标准浏览器中,我们直接使用children即可,但是这样获取的是一个元素集合(类数组),为了和IE6~8下保持一致,我们借用数组原型上的slice,实现把类数组转换为数组。
ary = Array.prototype.slice.call(curEle.children);
}
// --> 二次筛选
if (typeof tagName === 'string') {
for (var k = 0; k < ary.length; k++) { // 数组长度会减小的情况,必须要使用这样的方式判断长度,不能和上面一样
var curEleNode = ary[k];
if (curEleNode.nodeName.toLowerCase() !== tagName.toLowerCase()) {
// --> 不是我想要的标签
ary.splice(k, 1);
k --;
}
}
}
return ary;
}
这个方法中,判断是否是IE6~8用的是正则:/MSIE (6|7|8)/i.test(navigator.userAgent),而在封装DOM库的时候,在很多地方都要判断是否是IE6~8,我们可以每次都判断一次是不是IE6~8....不过,如果我们只判断一次,然后在所有的方法中都用,那是不是更好呢?....这就是JavaScript库封装中常用的惰性思想。
2.2、封装库的惰性思想
我们封装库的方式使用单例的方式,然后将单例放到一个自执行函数中,基本的形式为:
var utils = (function () {
function children(curEle, tagName) {
// ...
}
return {
children: children
}
})();
这时候我们可以将判断是否是IE6~8的方法,放在自执行函数中,然后在具体的方法中使用:
var utils = (function () {
var flag = "getComputedStyle" in window;
function children(curEle, tagName) {
if(!flag){
// IE6~8的情况
} else {
// 非IE6~8
}
}
return {
children: children
}
})();
在自执行函数中的flag变量不销毁,存储的是判断当前浏览器是否兼容getComputedStyle,兼容的话是标准浏览器,但是如果flag=false,说明当前浏览器是IE6~8。
惰性思想:惰性思想是JavaScript搞基编程技巧之一,在第一次给utils赋值的时候就已经把兼容处理好了,把最后结果存放在flag变量中,以后在每一个方法中,只要是IE6~8不兼容的,我们不需要重新检测,只需要使用flag的值即可。
那么用这种方式来改造当前的类库:
var utils = (function () {
var flag = "getComputedStyle" in window;
function children(curEle, tagName) {
var ary = [];
if (!flag) {
var nodeList = curEle.childNodes;
for (var i = 0, len = nodeList.length; i < len; i++) {
var curNode = nodeList[i];
curNode.nodeType === 1 ? ary[ary.length] = curNode : null;
}
nodeList = null;
} else {
ary = this.listToArray(curEle.children);
}
if (typeof tagName === "string") {
for (var k = 0; k < ary.length; k++) {
var curEleNode = ary[k];
if (curEleNode.nodeName.toLowerCase() !== tagName.toLowerCase()) {
ary.splice(k, 1);
k--;
}
}
}
return ary;
}
return {
children: children
}
})();
将之前几篇文章中封装的几个工具方法也加入该类库中(文章最后有该类库的下载地址):
2.3、获取上一个兄弟元素节点(prev)
首先获取当前元素的上一个哥哥节点,判断是否为元素节点,不是的话基于当前的节点继续找上面的哥哥节点.....一直找到哥哥元素节点为止,如果没有哥哥节点,返回null即可。
function prev(curEle) {
if (flag) {
return curEle.previousElementSibling;
}
var pre = curEle.previousSibling;
while (pre && pre.nodeType !== 1) {
pre = pre.previousSibling;
}
return pre;
}
2.4、获取下一个兄弟元素节点(next)
function next(curEle) {
if (flag) {
return curEle.nextElementSibling;
}
var nex = curEle.nextSibling;
while (nex && nex.nodeType !== 1) {
nex = nex.nextSibling;
}
return nex;
}
2.5、获取前面所有的兄弟元素节点(prevAll)
function prevAll(curEle) {
var ary = [];
var pre = this.prev(curEle);
while (pre) {
ary.unshift(pre);
pre = this.prev(pre);
}
return ary;
}
2.6、获取后面所有的兄弟元素节点(nextAll)
function nextAll(curEle) {
var ary = [];
var nex = this.next(curEle);
while (nex) {
ary.push(nex);
nex = this.next(nex);
}
return ary;
}
2.7、获取相邻的两个元素节点(sibling)
function sibling(curEle) {
var pre = this.prev(curEle);
var nex = this.next(curEle);
var ary = [];
pre ? ary.push(pre) : null;
nex ? ary.push(nex) : null;
return ary;
}
2.8、获取所有的兄弟元素节点(siblings)
function siblings(curEle) {
return this.prevAll(curEle).concat(this.nextAll(curEle));
}
2.9、获取当前元素的索引(index)
function index(curEle) {
return this.prevAll(curEle).length;
}
2.10、获取第一个元素子节点(firstChild)
function firstChild(curEle) {
var chs = this.children(curEle);
return chs.length > 0 ? chs[0] : null;
}
2.11、获取最后一个元素子节点(lastChild)
function lastChild(curEle) {
var chs = this.children(curEle);
return chs.length > 0 ? chs[chs.length - 1] : null;
}
三、操作元素的方法封装
3.1、向指定容器的末尾追加元素(append)
function append(newEle, container) {
container.appendChild(newEle);
}
3.2、向指定容器的开头追加元素(prepend)
把新的元素添加到容器中第一个子元素节点的前面,如果一个元素子节点都没有,就放在末尾即可。
function prepend(newEle, container) {
var fir = this.firstChild(container);
if (fir) {
container.insertBefore(newEle, fir);
return;
}
container.appendChild(newEle);
}
3.3、把新元素(newEle)追加到指定元素(oldEle)的前面(insertBefore)
function insertBefore(newEle, oldEle) {
oldEle.parentNode.insertBefore(newEle, oldEle);
}
3.4、把新元素(newEle)追加到指定元素(oldEle)的后面(insertAfter)
相当于追加到oldEle弟弟元素的前面,如果弟弟不存在,也就是当前元素已经是最后一个了,我们把新的元素放在最末尾即可。
function insertAfter(newEle, oldEle) {
var nex = this.next(oldEle);
if (nex) {
oldEle.parentNode.insertBefore(newEle, nex);
return;
}
oldEle.parentNode.appendChild(newEle);
}
四、操作样式的方法封装
4.1、验证当前元素中是否包含className这个样式类名(hasClass)
function hasClass(curEle, className) {
var reg = new RegExp("(^| +)" + className + "( +|$)");
return reg.test(curEle.className);
}
4.2、给元素增加样式类名(addClass)
function addClass(curEle, className) {
var ary = className.replace(/(^ +| +$)/g, "").split(/ +/g);
for (var i = 0, len = ary.length; i < len; i++) {
var curName = ary[i];
if (!this.hasClass(curEle, curName)) {
curEle.className += " " + curName;
}
}
}
4.3、给元素移除样式类名(removeClass)
function removeClass(curEle, className) {
var ary = className.replace(/(^ +| +$)/g, "").split(/ +/g);
for (var i = 0, len = ary.length; i < len; i++) {
var curName = ary[i];
if (this.hasClass(curEle, curName)) {
var reg = new RegExp("(^| +)" + curName + "( +|$)", "g");
curEle.className = curEle.className.replace(reg, " ");
}
}
}
4.5、通过元素的样式类名获取一组元素集合(getElementsByClass)
function getElementsByClass(strClass, context) {
context = context || document;
if (flag) {
return this.listToArray(context.getElementsByClassName(strClass));
}
//->IE6~8
var ary = [], strClassAry = strClass.replace(/(^ +| +$)/g, "").split(/ +/g);
var nodeList = context.getElementsByTagName("*");
for (var i = 0, len = nodeList.length; i < len; i++) {
var curNode = nodeList[i];
var isOk = true;
for (var k = 0; k < strClassAry.length; k++) {
var reg = new RegExp("(^| +)" + strClassAry[k] + "( +|$)");
if (!reg.test(curNode.className)) {
isOk = false;
break;
}
}
if (isOk) {
ary[ary.length] = curNode;
}
}
return ary;
}
五、样式的设置和获取相关的方法
5.1、获取元素的样式值(getCss)
function getCss(attr) {
var val = null, reg = null;
if (flag) {
val = window.getComputedStyle(this, null)[attr];
} else {
if (attr === "opacity") {
val = this.currentStyle["filter"];
reg = /^alpha\(opacity=(\d+(?:\.\d+)?)\)$/;
val = reg.test(val) ? reg.exec(val)[1] / 100 : 1;
} else {
val = this.currentStyle[attr];
}
}
reg = /^(-?\d+(\.\d+)?)(px|pt|em|rem)?$/;
return reg.test(val) ? parseFloat(val) : val;
}
5.2、给当前元素的某一个样式属性设置值(增加在行内样式上的)
function setCss(attr, value) {
if (attr === "float") {
this["style"]["cssFloat"] = value;
this["style"]["styleFloat"] = value;
return;
}
if (attr === "opacity") {
this["style"]["opacity"] = value;
this["style"]["filter"] = "alpha(opacity=" + value * 100 + ")";
return;
}
var reg = /^(width|height|top|bottom|left|right|((margin|padding)(Top|Bottom|Left|Right)?))$/;
if (reg.test(attr)) {
if (!isNaN(value)) {
value += "px";
}
}
this["style"][attr] = value;
}
5.3、给当前元素批量的设置样式属性值(setGroupCss)
function setGroupCss(options) {
for (var key in options) {
if (options.hasOwnProperty(key)) {
setCss.call(this, key, options[key]);
}
}
}
5.4、实现了获取、单独设置、批量设置元素的样式值(css)
function css(curEle) {
var argTwo = arguments[1], ary = Array.prototype.slice.call(arguments, 1);
if (typeof argTwo === "string") {
if (typeof arguments[2] === 'undefined') {
return getCss.apply(curEle, ary);
}
setCss.apply(curEle, ary);
}
argTwo = argTwo || 0;
if (argTwo.toString() === "[object Object]") {
setGroupCss.apply(curEle, ary);
}
}
六、总结
以上的所有方法中,方法名大部分和jQuery中的类似,getElementsByClass方法在jQuery中没有实现,但是jQuery的一部分选择器也是基于这个方法原理来实现的。
firstChild、lastChild在jQuery中没有。
所有代码的下载地址为:https://git.oschina.net/iceman5531/My_-Library.git
DOM操作和样式操作库的封装的更多相关文章
- jQuery学习之旅 Item3 属性操作与样式操作
本节将Dom元素的操作:属性操作.样式操作.设置和获取HTML,文本和值.Css-Dom操作. 1.属性操作 <input type="text" name="us ...
- 6、前端--DOM操作(查找标签、节点操作、获取值操作、class操作、样式操作、绑定事件、内置参数this)
DOM操作之查找标签 前缀关键字>>>:document # 基本查找(核心) document.getElementById 根据ID获取一个标签 document.getElem ...
- jQuery属性操作之类样式操作
类样式的操作:指对DOM属性className进行添加.移除操作.比如addClass().removeClass().toggleClass(). 1. addClass() 1.1 概述 $(se ...
- python 之 前端开发( jQuery选择器、筛选器、样式操作、文本操作、属性操作、文档操作)
11.5 jQuery 引入方式: 方式一:本地引入 <script src="jquery-3.3.1.min.js"></script> <scr ...
- DOM样式操作
CSS 到 DOM的抽象 通过操作 CSS 对应的 DOM对象来更新CSS样式 换肤操作 如何获取实际的样式(不仅有行内,更有页面和外联样式表中定义的样式) 样式表分为三类: 外联,页面,行内 内部样 ...
- JS 操作Dom节点之样式
为了提高用户体验,我们经常会动态修改Dom节点的样式,各种浏览器差异比较大,我们如何应对?不断尝试,不断总结~! 1. style.getComputedStyle.currentStyle 内嵌样式 ...
- JQuery DOM操作 、属性和CSS样式操作、其他函数
DOM操作 1.在div1内部最后追加一个节点 $("#div1").append("<img src='../01-HTML基本标签/img/Male.gif'/ ...
- jQuery使用(二):DOM样式操作和属性操作
DOM取值与赋值 .html() .text() .size() 1.html()方法类似原生DOM的属性innerHTML,不传入参数的时候默认为取指定元素内的HTML内容,包含前后空白文本结构,以 ...
- DOM操作之属性和样式操作
在DOM操作,除了前面的节点操作以外,常常被用到的操作还有属性操作和节点操作,下面,主要来总结一下jQuery中的属性操作方法和样式操作方法. 在开始操作前,我们需要先在html中添加如下代码,后面所 ...
随机推荐
- js模块化开发——模块的写法
随着网站逐渐变成"互联网应用程序",嵌入网页的Javascript代码越来越庞大,越来越复杂. 网页越来越像桌面程序,需要一个团队分工协作.进度管理.单元测试等等......开发者 ...
- .NET DLL 保护措施详解(非混淆加密加壳)核心思路的实现
最近有很多朋友通过BLOG找到我询问我的相关细节,其实相关的实现细节我早已把源码上传到51aspx上面了,地址是http://www.51aspx.com/code/codename/56949 也有 ...
- jQuery表单对象属性过滤选择器
jQuery表单对象属性过滤选择器 <div id="p1" attr="p1"> <input type="text" ...
- jQuery 对象与Dom 对象互转
jQuery 对象与Dom 对象互转: $obj --[i],get(i)-->obj --$(obj)-->$obj; obj--$($(obj))-->$obj,多包装了也是$o ...
- AFN
一.什么是AFN 全称是AFNetworking,是对NSURLConnection的一层封装 虽然运行效率没有ASI高,但是使用比ASI简单 在iOS开发中,使用比较广泛 AFN的github地址 ...
- ADO.NET 数据库操作类
操作数据类 避免代码重用.造对象太多.不能分工开发 利用面向对象的方法,把数据访问的方式优化一下,利用封装类 一般封装成三个类: 1.数据连接类 提供数据连接对象 需要引用命名空间: using ...
- HDU 3782 xxx定律
xxx定律 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi ...
- 日月如梭,玩转JavaScript日期
一.Date对象 下面出现的源码都可以codepen在线查看. 1)时间戳毫秒计算 Date对象是基于"1970-01-01 08:00:00"到指定日期的毫秒数,不是" ...
- Dynamics CRM 2015-Auto Save
Auto Save,顾名思义,就是不需要明确地点击Save按钮,自动保存.这个功能在创建CRM Organization的时候,默认是开启的. 需要注意的是: 1. Auto Save适用于Main ...
- HTML__post 和 get区别【URL】
一.get和post的区别: 表单提交中get和post方式的区别有5点 1.get是从服务器上获取数据,post是向服务器传送数据. 2.get是把参数数据队列加到提交表单的ACTION属性所指的U ...