1、DOM封装-children
DOM库封装
- css方法的封装
- children方法的封装
- getElementsByClassName兼容处理
- addClass、removeClass的封装
- js惰性编程思想
- 使用自己的DOM库完成选项卡
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>DOM封装-children</title>
</head>
<body>
<div id="box">
<p>
<span></span>
</p>
<p></p>
<p></p>
<!--以下是SPAN-->
<!--以下是SPAN-->
<span></span>
<span></span>
<span></span>
</div>
<script src="js/utils.js"></script>
<script src="js/2-1.js"></script>
</body>
</html>
2-1.js
// console.log(box.children);//获取子节点 在IE6~8中浏览器会把注释节点当做元素节点处理
// console.log(box.childNodes);//获取所有子节点
// console.log(box.children.length);
/*兼容性:box.children 在IE6~8中浏览器会把注释节点当做元素节点处理*/
// =>children:不管在什么浏览器中都可以获取到当前元素的所有元素子节点(不包含注释)
//---------------------------------获取子节点兼容写法-------------------------------------------
// 法一:通过children获取子节点,除掉注释节点
function children(curEle) {
var childList = curEle.children;
childList = utils.toArray(childList);//把childList转成数组
for (var i = 0; i < childList.length; i++) {
var item = childList[i];
if (item.nodeType !== 1) {
childList.splice(i, 1);//删除当前这一项
i--;//当前删除后索引需要--,下次从当前这个位置循环
}
}
return childList;
}
console.log(children(box).length);
//----------------------------------------------------------------------------
// 法二:通过childNodes获取所有子节点,只保留元素子节点( item.nodeType === 1 )
function children(curEle) {
var result = [],
childList = curEle.childNodes;
for (var i = 0; i < childList.length; i++) {
var item = childList[i];
item.nodeType === 1 ? result.push(item) : null;
}
return result;
}
console.log(children(box).length);
//-------------------------------------------获取指定元素中的所有“元素子节点”---------------------------------
//=>我们可以获取指定元素中的所有“元素子节点”,并且我们还可以指定标签名,例如:我指定标
// 签名是'span',我们把所有叫做span的元素子节点获取即可
function children(curEle, tagName) {
var result = [],
childList = curEle.childNodes;
for (var i = 0; i < childList.length; i++) {
var item = childList[i];
item.nodeType === 1 ? result.push(item) : null;
}
//=>在获取的所有元素子节点中进行二次过滤
if (typeof tagName !== 'undefined') {
for (var k = 0; k < result.length; k++) {
// 当前遍历元素的子节点(result[k])的标签名(tagName)
// (得到的标签名都是大写的)和传进来的标签名(tagName,传进来的标签名也可能是大写)不相等,就删除。
if (result[k].tagName.toLowerCase() !== tagName.toLowerCase()) {
result.splice(k, 1);
k--;
}
}
}
return result;
}
console.log(children(box, 'span').length);
//----------------------------------------------------------------------------
// 整合以上代码
function children(curEle, tagName) {
var result = [],
childList = curEle.childNodes;//获取所有子节点
for (var i = 0; i < childList.length; i++) {//遍历获取到的子节点
var item = childList[i];
if (item.nodeType === 1) {// item.nodeType === 1 只保留元素子节点
if (typeof tagName !== 'undefined') { // 传了tagNname
if (item.tagName.toLowerCase() === tagName.toLowerCase()) {
result.push(item);
}
continue;//循环遍历,item.nodeType === 1才执行result.push(item);但是
// item.nodeType===1,传了tagNname,continue:就不再执行到result.push(item)
}
result.push(item);//条件item.nodeType === 1成立,条件typeof tagName !== 'undefined'
// 不成立(没传tagName),执行这一步
}
}
return result;
}
console.log(children(box).length);
console.log(children(box, 'span').length);
2、DOM封装-getElementsByClassName兼容处理之传递一个样式类
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>DOM封装-getElementsByClassName兼容处理之传递一个样式类</title>
</head>
<body>
<div class="box1 box2">1</div>
<div class="box2 box3">2</div>
<div class="box1 box2 box3" id="div3">3</div>
<div class="box1 box3">4</div>
<div class="box3">5</div>
<div class="box2">6</div>
<script src="js/utils.js"></script>
<script src="js/3-1.js"></script>
</body>
</html>
3-1.js
/*
* [context].getElementsByClassName([className])
* 在指定的上下文中,通过样式类名获取一组元素集合,IE6~8下不兼容
*/
// console.log(document.getElementsByClassName(' box1 '));
// console.log(document.getElementsByClassName('box2 box1'));//=>如果传递多个样式类名:同时具备这些样式类的元素(多个样式类之间加几个空格都无所谓,而且不计较编写的顺序)
//=>只处理传递一个样式类名的(但是首尾可能会加很多的空格)
//strClass:传递进来的样式类名,例如:'box1'
//context:限定获取的范围(上下文),不传递默认赋值为document
//=>思路:获取指定上下文中所有的标签,然后遍历这些标签,把所有CLASS中,包含传递进来的样式类的元素,都保存起来即可
function getEleByClass(strClass, context) {
context = context || document;//不传context默认上下文为document
var result = [],//存放最终的结果
nodeList = context.getElementsByTagName('*');//获取指定上下文里面的所有标签
strClass = strClass.replace(/^\s+|\s+$/g, '');//=>去除传递进来样式类的首尾空格
for (var i = 0; i < nodeList.length; i++) {//循环得到的所有标签
var item = nodeList[i];//每次遍历的标签
//=>当前项的CLASS:item.className
//=>传递进来的样式类:strClass
//=>我们接下来要验证item.className字符串中是否包含传递进来的strClass样式类
var reg = new RegExp('(^| +)' + strClass + '( +|$)')
if (reg.test(item.className)) {
result.push(item);
}
}
return result;
}
console.log(getEleByClass('box1'));
console.log(getEleByClass('box'));
console.log(getEleByClass(' box1 '));
//验证 "box1 box2 box3"(item.className) 是否包含 "box2"(strClass)
/*
* indexOf:虽然可以验证字符串是否包含某个字符,但是无法判断是否是包含这个样式类
* "box100 box2 box3".indexOf('box1') =>0
* 但是当前样式类中并没有box1这个样式类
*/
//=> /box2/ =>只要包含box2这个字符就可以(和indexOf类似了)
//=> /\bbox2\b/ =>这个说明它是完整的单词 遇到这种字符串就不可以了'box1 box2-2 box3' 在正则中\b不仅是单词的边界,而且它会把中杠两边也作为边界 'box2-2'和/\bbox2\b/是匹配的,但是当前元素样式类是box2-2而不是box2
//=>/(^| +)box2( +|$)/ 中间是box2完单词,左右两边是空格或者开始结束
// var reg = new RegExp('(^| +)' + strClass + '( +|$)');//正则要包含传递进来的形参strClass,想在一个正则中包含某个变量值属于正则里面的规则是动态设定的,字面量 /'+strClass+'/ 方式不行,字面量方式里面所有的东西都是原字符,所以只能用new 构造函数
//法二:不用正则 把获取到的item.className拆成数组循环遍历--------------------------------------------------------------------------------------------------------------------
function getEleByClass(strClass, context) {
context = context || document;
var result = [],
nodeList = context.getElementsByTagName('*');
strClass = strClass.replace(/^\s+|\s+$/g, '');
for (var i = 0; i < nodeList.length; i++) {
var item = nodeList[i];
var ary = item.className.split(/ +/),//以多个空格把item.className拆成数组 'box1 box2 box3' => ['box1','box2','box3']
flag = false;
for (var j = 0; j < ary.length; j++) {
if (strClass === ary[j]) {
flag = true;
break;
}
}
flag ? result.push(item) : null;
}
return result;
}
console.log(getEleByClass('box1'));
console.log(getEleByClass('box'));
console.log(getEleByClass(' box1 '));
//法三:不用正则 把获取到的item.className拆成数组循环遍历 但是indexOf不兼容--------------------------------------------------------------------------------------------------------------------
function getEleByClass(strClass, context) {
context = context || document;
var result = [],
nodeList = context.getElementsByTagName('*');
strClass = strClass.replace(/^\s+|\s+$/g, '');
for (var i = 0; i < nodeList.length; i++) {
var item = nodeList[i];
var ary = item.className.split(/ +/);//以多个空格把item.className拆成数组 'box1 box2 box3' => ['box1','box2','box3']
if(ary.indexOf(strClass) > -1){//不兼容
result.push(item);
}
}
return result;
}
console.log(getEleByClass('box1'));
console.log(getEleByClass('box'));
console.log(getEleByClass(' box1 '));
3、DOM封装-getElementsByClassName兼容处理之传递多个样式类名
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>DOM封装-getElementsByClassName兼容处理之传递多个样式类名</title>
</head>
<body>
<div class="box1 box2">1</div>
<div class="box2 box3">2</div>
<div class="box1 box2 box3" id="div3">3</div>
<div class="box1 box3">4</div>
<div class="box3">5</div>
<div class="box2">6</div>
<script src="js/utils.js"></script>
<script src="js/3-2.js"></script>
</body>
</html>
3-2.js 假设法
function getEleByClass(strClass, context) {
context = context || document;
var result = [],
nodeList = context.getElementsByTagName('*');
//=>不仅要去除首尾空格,还要把传递的多样式类名以空格(多空格)拆分成一个数组(数组中包含传递的每一个样式类名)
strClass = strClass.replace(/^\s+|\s+$/g, '').split(/ +/);
for (var i = 0; i < nodeList.length; i++) {
var item = nodeList[i],
itemClass = item.className,
flag = true;//=>假设你传递的样式类名在ITEM中都存在
//=>验证假设的值是真还是假:循环传递的所有样式类名,拿每一个传递的样式类名和当前标签的CLASS-NAME比较,只要有一个不在标签中,我们假设的值就是错误的
for (var k = 0; k < strClass.length; k++) {
var reg = new RegExp('(^| +)' + strClass[k] + '( +|$)');
if (!reg.test(itemClass)) {
flag = false;
break;
}
}
// console.log(nodeList[i].className)
//=>如果FLAG为TRUE就存储
flag ? result.push(item) : null;
}
return result;
}
console.log(getEleByClass(' box2 box1 '))
console.log(getEleByClass(' box2 '))
console.log(getEleByClass(' box2 box1 box3 '))
可以通过断点看js走势:
www.cnblogs.com/mqfblog/p/5… js断点调试心得
function getEleByClass(strClass, context) {
context = context || document;
var nodeList = context.getElementsByTagName('*');
console.log(nodeList);
strClass = strClass.replace(/^ +| +$/g, '').split(/ +/);
//=>排除法:先拿第一个样式类在所有的标签中比较,把没有当前样式类的都干掉,和第一个比较完成后,再和第二个传递进来的样式类比较,没有的接着干掉...
nodeList = utils.toArray(nodeList);//类数组转换成数组
for (var i = 0; i < strClass.length; i++) {
var reg = new RegExp('(^| +)' + strClass[i] + '( +|$)');
for (var k = 0; k < nodeList.length; k++) {
if (!reg.test(nodeList[k].className)) {
nodeList.splice(k, 1);
k--;
}
}
}
return nodeList;
}
getEleByClass(' box2 box1 box3 ')
3-4.js
假设法,做一个完善
function getEleByClass(strClass, context) {
context = context || document;
if ('getElementsByClassName' in document) {
return utils.toArray(context.getElementsByClassName(strClass));
}
//以上是标准写法(为了保证和兼容保证统一,以上也把类数组转换成数组),以下是兼容ie6~8的写法。
var result = [],
nodeList = context.getElementsByTagName('*');
strClass = strClass.replace(/^\s+|\s+$/g, '').split(/ +/);
for (var i = 0; i < nodeList.length; i++) {
var item = nodeList[i],
itemClass = item.className,
flag = true;
for (var k = 0; k < strClass.length; k++) {
var reg = new RegExp('(^| +)' + strClass[k] + '( +|$)');
if (!reg.test(itemClass)) {
flag = false;
break;
}
}
flag ? result.push(item) : null;
}
return result;
}
getEleByClass(' box2 box1 box3 ')
//-------------------------------------------------------------------------------------
//为了能element.queryElementsByClassName()去使用这个方法,我们需要把这个方法扩展到内置dom类的原型上,
//由于所有能操作方法的dom元素最终都会走到Node这个内置类,所以我们需要在Node这个内置类去扩展(一般项目中不这么用,
// 修改内置类这种操作很危险)
Node.prototype.queryElementsByClassName = function queryElementsByClassName() {
if (arguments.length === 0) return [];
var strClass = arguments[0],
nodeList = utils.toArray(this.getElementsByTagName('*'));//这里的上下文就是this
strClass = strClass.replace(/^ +| +$/g, '').split(/ +/);
for (var i = 0; i < strClass.length; i++) {
var reg = new RegExp('(^| +)' + strClass[i] + '( +|$)');
for (var k = 0; k < nodeList.length; k++) {
if (!reg.test(nodeList[k].className)) {
nodeList.splice(k, 1);
k--;
}
}
}
return nodeList;
};
document.queryElementsByClassName('box2 box1 ');//queryElementsByClassName()中的this是document,也是上下文
document.queryElementsByClassName();
box.queryElementsByClassName();//queryElementsByClassName()中的this是box,也是上下文
4、DOM封装-基于惰性思想的高级单例模式优势和作用
//单例模式
var utils={}
//基于惰性思想的高级单例模式,(形成一个不销毁的作用域)
var utils=(function(){
return {};
})();
什么是js的惰性思想
//2、=>JS惰性思想:懒惰性的思想,能一次解决的,绝对不会每一次都重新的处理,属于JS性能优化的一种
var utils = (function () {//自执行函数形成一个不销毁私有作用域,有一个私有变量flag
var flag = 'getComputedStyle' in window;//=>再给UTILS赋值的时候,我们验证了window中是否存在getComputedStyle这属性,如果存在,说明当前的浏览器是标准浏览器,反之说明是IE6~8,不需要再每次都去验证当前的浏览器是否兼容
//用'getElementsByClassName' in document也行,ie6~8也不兼容getElementsByClassName
function getCss(curEle, attr) {
var value = null;
if (flag) {
value = window.getComputedStyle(curEle, null)[attr];
} else {
}
}
return {
getCss: getCss
}
})();
//1、单例模式 每执行一遍utils.getCss()方法,就要判断一次兼不兼容(不管执行多少次都要去判断这个浏览器兼不兼容,其实第一次执行的
//的时候就已经知道兼不兼容,后面不需要再去判断这个浏览器兼不兼容,直接把第一次判断出来的结果拿来用就行了)
// var utils = {
// getCss: function (curEle, attr) {
// var value = null;
// if (window.getComputedStyle) {
// value = window.getComputedStyle(curEle, null)[attr];
// } else {
// value = curEle.currentStyle[attr];
// }
// return value;
// }
// };
console.log(utils.getCss(box, 'margin'));
console.log(utils.getCss(box, 'padding'));
//...
所以我们把utils.js用js的惰性思想来封装:通过闭包(自执行函数形成闭包),变量保存需要获取的判断值来实现
//高级单例模式基于惰性思想的好处
//1、高级单例模式是基于惰性思想来实现性能优化,把需要做兼容处理的验证提前获取到执行一次就可以了,
//以后想用直接拿过来用就行,不需要从新做兼容判断(变量保存验证值)
// 2、单例模式中的很多方法,只暴露需要暴露的方法
var utils = (function () {
var isCompatible = 'getElementsByClassName' in document,//ie6~8不兼容getElementsByClassName
isSupportJSON = 'JSON' in window;//ie8及ie8以上兼容JSON ie6~7不兼容
//=>toArray & toJSON
var toArray = function (classAry) {
var ary = [];
// // 用try catch不管浏览器兼不兼容ie6~8,都得执行一遍 ary = Array.prototype.slice.call(classAry);性能浪费
// try{
// ary = Array.prototype.slice.call(classAry);
// }catch(e){
// for (var i = 0; i < classAry.length; i++) {
// ary[ary.length] = classAry[i];
// }
// }
if (isCompatible) {//直接通过isCompatible判断兼不兼容,不需要再从新判断
ary = Array.prototype.slice.call(classAry);
} else {
for (var i = 0; i < classAry.length; i++) {
ary[ary.length] = classAry[i];
}
}
return ary;
};
var toJSON = function (str) {
return isSupportJSON ? JSON.parse(str) : eval('(' + str + ')');
};
//=>offset & winBox
var offset = function (curEle) {
var l = curEle.offsetLeft,
t = curEle.offsetTop,
p = curEle.offsetParent;
while (p.tagName !== 'BODY') {
// if(!/MISE 8/.test(navigator.userAgent))){
//isCompatible === false=>ie6~8 isSupportJSON === true=>肯定不是ie6、7
if (isCompatible === false && isSupportJSON === true) {
l += p.clientLeft;
t += p.clientTop;
}
l += p.offsetLeft;
t += p.offsetTop;
p = p.offsetParent;
}
return {left: l, top: t};
};
var winBox = function (attr, value) {
if (typeof value !== 'undefined') {
document.documentElement[attr] = value;
document.body[attr] = value;
return;
}
return document.documentElement[attr] || document.body[attr];
};
//=>children
function children(ele, attr) {
var ary = [];
// 'getComputedStyle' in window ? ary = toArray(ele.children) : ary = toArray(ele.childNodes);
isCompatible ? ary = toArray(ele.children) : ary = toArray(ele.childNodes);
for (var k = 0; k < ary.length; k++) {
var obj = ary[k];
if (obj.nodeType === 1) {
if (attr && attr.toLowerCase() !== obj.tagName.toLowerCase()) {
ary.splice(k, 1);
k--;
}
} else {
ary.splice(k, 1);
k--;
}
}
return ary;
}
//=>getElementsByClassName
function getElementsByClassName(classStr, context) {
if (arguments.length === 0) return [];
context = context || document;
// if(document.getElementsByClassName){
if (isCompatible) {
return toArray(context.getElementsByClassName(classStr));
}
var eleList = toArray(context.getElementsByTagName("*"));
var classList = classStr.replace(/^ +| +$/g, "").split(/ +/);
for (var i = 0; i < classList.length; i++) {
var cur = classList[i];
var reg = new RegExp("(^| +)" + cur + "( +|$)");
for (var j = 0; j < eleList.length; j++) {
if (!reg.test(eleList[j].className)) {
eleList.splice(j, 1);
j--;
}
}
}
return eleList;
}
//=>css
function getCss(curEle, attr) {
var value = null, reg = null;
// if('getComputedStyle' in window){
if (isCompatible) {
value = window.getComputedStyle(curEle, null)[attr];
} else {
if (attr === 'opacity') {
value = curEle.currentStyle['filter'];
reg = /^alpha\(opacity=(.+)\)$/i;
return reg.test(value) ? reg.exec(value)[1] / 100 : 1;
}
value = curEle.currentStyle[attr];
}
reg = /^-?\d+(.\d+)?(pt|px|rem|em)?$/i;
return reg.test(value) ? parseFloat(value) : value;
}
function setCss(curEle, attr, value) {
if (attr === 'opacity') {
curEle.style.opacity = value;
curEle.style.filter = 'alpha(opacity=' + value * 100 + ')';
return;
}
!isNaN(value) && !/(fontWeight|lineHeight|zoom|zIndex)/i.test(attr) ? value += 'px' : null;
curEle.style[attr] = value;
}
function setGroupCss(curEle, options) {
if (Object.prototype.toString.call(options) !== '[object Object]') return;
for (var attr in options) {
if (options.hasOwnProperty(attr)) {
setCss(curEle, attr, options[attr])
}
}
}
function css() {
var len = arguments.length,
type = Object.prototype.toString.call(arguments[1]),
fn = getCss;
len >= 3 ? fn = setCss : (len === 2 && type === '[object Object]' ? fn = setGroupCss : null)
return fn.apply(this, arguments);
}
return {
toArray: toArray,
toJSON: toJSON,
offset: offset,
winBox: winBox,
children: children,
getElementsByClassName: getElementsByClassName,
css: css
}
})();
5、DOM封装-惰性思想之函数重写
项目中惰性思想还有一种形式: 函数重写的惰性思想
var _flag = window.getComputedStyle;
function getCss(curEle, attr) {
if (_flag) {//每次进来之后也得从新验证_flag,只是不需要再获取window.getComputedStyle,也需要去验证(这是通过变量先获取到判断值的惰性思想),所以还有种惰性思想:函数覆盖
getCss = function (curEle, attr) {
return window.getComputedStyle(curEle, null)[attr];
}
} else {
getCss = function (curEle, attr) {
return curEle.currentStyle[attr];
}
}
return getCss(curEle, attr);//=>此处执行的GET-CSS是其中的某一个小方法了,把得到的结果返回
}
//----------------------------------------------------------------------------------
// 函数覆盖的惰性思想,比以上写法性能上更优化
function getCss(curEle, attr) {
if (window.getComputedStyle) {
getCss = function (curEle, attr) {
return window.getComputedStyle(curEle, null)[attr];
}
} else {
getCss = function (curEle, attr) {
return curEle.currentStyle[attr];
}
}
return getCss(curEle, attr);//=>此处执行的GET-CSS是其中的某一个小方法了,把得到的结果返回
}
//=>getCss:最外层大方法
//=>第一次执行GET-CSS
//[标准]
// getCss = function (curEle, attr) {
// return window.getComputedStyle(curEle, null)[attr];
// }
//[IE6~8]
// getCss = function (curEle, attr) {
// return curEle.currentStyle[attr];
// }
// 第一次执行GET-CSS 不仅把最外层大方法getCss改成其中的某一个小方法,也得到了结果,所以第二次执行根据当前浏览器直接
// 执行其中的某一个小方法,不需要再去做兼容处理验证,这就是函数重写的惰性思想。
console.log(getCss(box, 'padding'));
哪种需要用函数重写的惰性思想:凡是加载一个方法,进来首先要验证兼容不兼容,然后再怎么做的,都可以重写这个函数,第一次 执行大方法的时候根据兼容性问题把大方法从写成某一个小方法,以后只要浏览器不变,再执行就会直接执行对应的小方法,不需要再做兼容判断处理,更能提高性能
凡是这个方法后期要执行很多次,第一次执行性能可能会消耗一点,但是后期想要性能好,都可以用这种重写的惰性思想来实现,把需要验证的值(window.getComputedStyle)也可以提取出来用一个公共变量现保存起来,来达到js的性能优化
6、重写选项卡-原有选项卡实现的弊端
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>-选项卡</title>
<link rel="stylesheet" href="css/reset.min.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div class="tabBox" id="tabBox">
<ul class="tab clearfix">
<li class="select">新闻</li>
<li>电影</li>
<li>动漫</li>
</ul>
<div class="con select">1</div>
<div class="con">2</div>
<div class="con">3</div>
</div>
<script src="js/utils.js"></script>
<script src="js/index.js"></script>
</body>
</html>
css
.tabBox {
margin: 20px auto;
width: 500px;
font-size: 14px;
}
.tabBox .tab {
position: relative;
top: 1px;
}
.tabBox .tab li {
float: left;
margin-right: 15px;
padding: 0 15px;
height: 35px;
line-height: 35px;
border: 1px solid #999;
background: #EEE;
cursor: pointer;
}
.tabBox .tab li.select {
background: #FFF;
border-bottom-color: #FFF;
}
.tabBox .con {
display: none;
padding: 10px;
height: 150px;
border: 1px solid #999;
}
.tabBox .con li {
line-height: 29px;
border-bottom: 1px dashed #CCC;
}
.tabBox .con.select {
display: block;
}
.tabBox2 {
background: lightcyan;
}
reset.min.css
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,button,input,textarea,th,td{margin:0;padding:0}body{font-size:12px;font-style:normal;font-family:"\5FAE\8F6F\96C5\9ED1",Helvetica,sans-serif}small{font-size:12px}h1{font-size:18px}h2{font-size:16px}h3{font-size:14px}h4,h5,h6{font-size:100%}ul,ol{list-style:none}a{text-decoration:none;background-color:transparent}a:hover,a:active{outline-width:0;text-decoration:none}table{border-collapse:collapse;border-spacing:0}hr{border:0;height:1px}img{border-style:none}img:not([src]){display:none}svg:not(:root){overflow:hidden}html{-webkit-touch-callout:none;-webkit-text-size-adjust:100%}input,textarea,button,a{-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]),video:not([controls]){display:none;height:0}progress{vertical-align:baseline}mark{background-color:#ff0;color:#000}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}button,input,select,textarea{font-size:100%;outline:0}button,input{overflow:visible}button,select{text-transform:none}textarea{overflow:auto}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.clearfix:after{display:block;height:0;content:"";clear:both}
js
//法一:-------------------------------------------------------
var tabBox = document.getElementById('tabBox'),
tabList = tabBox.getElementsByTagName('li'),
conList = tabBox.getElementsByTagName('div');
for(var i = 0; i < tabList.length; i++){
var curTab = tabList[i];
curTab.myIndex = i;
curTab.onclick = function(){
//=>清楚所有li和div的选中样式(但是这样每次清楚所有的浪费性能,只需要清除上一个li和div)
for(var k = 0; k < tabList.length; k++){
tabList[k].className = null;
conList[k].className = 'con';
}
//=>让当前操作的li和div有选中样式
this.className='select';
conList[this.myIndex].className = 'con select';
}
}
// 法二:-------------------------------------------------------
// 清除上一个li和div(性能好点,但是通过getElementsByTagName获取元素时,会获取到所有后代元素,
// 项目中每个con里面假设还有ul、li,这样通过getElementsByTagName去获取,会获取后代中所有标签名为li或者div的
// 元素,而不是单纯的就这几个页卡而已)
var tabBox = document.getElementById('tabBox'),
tabList = tabBox.getElementsByTagName('li'),
conList = tabBox.getElementsByTagName('div');
var _prevIndex = 0;//记录上一次选中li的索引(初始选中第一个,所以默认值是零)
for(var i = 0; i < tabList.length; i++){
var curTab = tabList[i];
curTab.myIndex = i;
curTab.onclick = function(){
//=> 先把上一个清掉即可
tabList[_prevIndex].className = null;
conList[_prevIndex].className = 'con';
//=>当前点击有选中
this.className = 'select';
conList[this.myIndex].className = 'con select';
//=>当前点击这一项是下一次点击的上一项
_prevIndex = this.myIndex;
}
}
7、重写选项卡-使用DOM库完成选项卡的封装
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>-选项卡</title>
<link rel="stylesheet" href="css/reset.min.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div class="tabBox" id="tabBox">
<ul class="tab clearfix">
<li>新闻</li>
<li>电影</li>
<li>动漫</li>
</ul>
<div class="con">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
<div class="con">
<ul>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
</div>
<div class="con">
<ul>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
</ul>
</div>
</div>
<script src="js/utils.js"></script>
<script src="js/index201711141717.js"></script>
</body>
</html>
js
//------------------------------------------------------------
var tabBox = document.getElementById('tabBox'),
//由于通过getElementsByTagName获取元素时,会获取到所有后代元素,
//所以通过样式类名去获取子元素,这里用utils里面封装好的方法
tab = utils.getElementsByClassName('tab', tabBox)[0],
tabList = utils.children(tab, 'li'),
conList = utils.children(tabBox, 'div'),
_prevIndex = 0;
for (var i = 0; i < tabList.length; i++) {
tabList[i].myIndex = i;
tabList[i].onclick = function () {
tabList[_prevIndex].className = null;
conList[_prevIndex].className = 'con';
this.className = 'select';
conList[this.myIndex].className = 'con select';
_prevIndex = this.myIndex;
}
}
//------------------------------------------------------------
// 初步批量实现某一个相同操作,不算是封装插件
~function () {
var tabBoxList = utils.getElementsByClassName('tabBox');
for (var i = 0; i < tabBoxList.length; i++) {
change(tabBoxList[i]);
}
function change(tabBox) {
var tab = utils.getElementsByClassName('tab', tabBox)[0],
tabList = utils.children(tab, 'li'),
conList = utils.children(tabBox, 'div'),
_prevIndex = 0;
for (var i = 0; i < tabList.length; i++) {
tabList[i].myIndex = i;
tabList[i].onclick = function () {
tabList[_prevIndex].className = null;
conList[_prevIndex].className = 'con';
this.className = 'select';
conList[this.myIndex].className = 'con select';
_prevIndex = this.myIndex;
}
}
}
}();
/*html结构要一样
<div class='tabBox'>
<ul class='tab'>
<li class='select'></li>
<li>...</li>
...
</ul>
<div class='con select'>...</div>
<div class='con'>...</div>
</div>
*/
8、重写选项卡-使用构造函数初步封装选项卡插件
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>-选项卡</title>
<link rel="stylesheet" href="css/reset.min.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div class="tabBox" id="tabBox">
<ul class="tab clearfix">
<li>新闻</li>
<li>电影</li>
<li>动漫</li>
</ul>
<div class="con">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
<div class="con">
<ul>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
</div>
<div class="con">
<ul>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
</ul>
</div>
</div>
<div class="tabBox tabBox2">
<ul class="tab clearfix">
<li>新闻</li>
<li>电影</li>
<li>动漫</li>
</ul>
<div class="con">
1
</div>
<div class="con">
2
</div>
<div class="con">
3
</div>
</div>
<div class="tabBox">
<ul class="tab clearfix">
<li>新闻</li>
<li>电影</li>
<li>动漫</li>
<li>科技</li>
</ul>
<div class="con">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
<div class="con">
<ul>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
</div>
<div class="con">
<ul>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
</ul>
</div>
<div class="con">
<ul>
<li>16</li>
<li>17</li>
<li>18</li>
<li>19</li>
<li>20</li>
</ul>
</div>
</div>
<script src="js/utils.js"></script>
<script src="js/index.js"></script>
</body>
</html>
js
封装选项卡插件,封装插件、组件、类库必用构造函数模式
封装插件的思想:先把一个功能实现,以后调用它就能实现别的
function ChangeTab(tabBox) {//1、构造函数创建一个ChangeTab类
//=>3、技巧:为了保证这些值在类的任何方法中都可以调取使用,我们一般都把信息值存放在当前实例上(THIS):只要保证每个方法中的THIS都是当前类的实例,我们就可以获取这些值了
// 构造函数封装的时候,我们一般会把常用的公共用的东西写在this上(也就是当前实例上)
this.tabBox = tabBox;
//=>5、开始实现选项卡的功能
// ChangeTab.prototype.init().call(this);
this.init();
}
ChangeTab.prototype = {//2、往类的原型上写方法,(重构自定义类的原型,需要保证constructor: ChangeTab)
constructor: ChangeTab,
//7、实现页卡切换
change:function(){
// var tabBox = this.tabBox,
// tab = this.tab,
// tabList = this.tabList,
// conList = this.conList,
// prevIndex = this.prevIndex,
// _this = this;
// for (var i = 0; i < tabList.length; i++) {
// tabList[i].myIndex = i;
// tabList[i].onclick = function () {
// //=>this:当前点击的这个li,不是实例(_this是实例)
// tabList[prevIndex].className = '';
// conList[prevIndex].className = 'con';
// this.className = 'select';
// conList[this.myIndex] = 'con select';
// _this.prevIndex = prevIndex = this.myIndex;
// }
// }
// 实际项目中开发写法如下:不定义变量,都通过this直接操作
var _this = this;
for (var i = 0; i < _this.tabList.length; i++) {
_this.tabList[i].myIndex = i;
_this.tabList[i].onclick = function () {
//=>this:当前点击的这个LI,不是实例(_this是实例)
_this.tabList[_this.prevIndex].className = '';
_this.conList[_this.prevIndex].className = 'con';
this.className = 'select';
_this.conList[this.myIndex].className = 'con select';
_this.prevIndex = this.myIndex;
}
}
},
//4 init初始化,完成选项卡的操作
init: function () {
//=>6、获取当前页卡区域中的元素(LI & DIV) 把获取到的值都通过this挂载到这个实例上去,以后再其他方法中也能用
// var tabBox = this.tabBox,
// tab = utils.children(tabBox, 'ul')[0],
// tabList = utils.children(tab, 'li'),
// conList = utils.children(tabBox, 'div'),
// this.tab = tab;
// this.tabList = tabList;
// this.conList = conList;
// this.prevIndex = 0;
//=>直接通过this写到实例上
var tabBox = this.tabBox,
this.tab = utils.children(this.tabBox, 'ul')[0],
this.tabList = utils.children(this.tab, 'li'),
this.conList = utils.children(this.tabBox, 'div'),
this.prevIndex = 0;
// =>7、实现页卡切换
this.change();
}
};
使用构造函数封装插件,核心点都在于实例this,所有值和方法的共享都是通过this来实现的,构造函数中的this就是当前实例
只要new ChangeTab()就会执行this.init(),
new ChangeTab,this就是构造函数ChangeTab的实例,
不new ChangeTab,直接执行ChangeTab(),this是window,第一次执行和第二次执行ChangeTab传的值都是window,会冲突,所已要new ChangeTab()执行,
为了防止冲突,我们把它封装成闭包,但是外面要用这个类,我们通过window暴露出去
~function(){
function ChangeTab(tabBox) {//1、构造函数创建一个ChangeTab类
//=>3、技巧:为了保证这些值在类的任何方法中都可以调取使用,我们一般都把信息值存放在当前实例上(THIS):只要保证每个方法中的THIS都是当前类的实例,我们就可以获取这些值了
// 构造函数封装的时候,我们一般会把常用的公共用的东西写在this上(也就是当前实例上)
this.tabBox = tabBox;
//=>5、开始实现选项卡的功能
// ChangeTab.prototype.init().call(this);
this.init();
}
ChangeTab.prototype = {//2、往类的原型上写方法,(重构自定义类的原型,需要保证constructor: ChangeTab)
constructor: ChangeTab,
//7、实现页卡切换
change:function(){
// 实际项目中开发写法如下:不定义变量,都通过this直接操作
var _this = this;
for (var i = 0; i < _this.tabList.length; i++) {
_this.tabList[i].myIndex = i;
_this.tabList[i].onclick = function () {
//=>this:当前点击的这个LI,不是实例(_this是实例)
_this.tabList[_this.prevIndex].className = '';
_this.conList[_this.prevIndex].className = 'con';
this.className = 'select';
_this.conList[this.myIndex].className = 'con select';
_this.prevIndex = this.myIndex;
}
}
},
//4 init初始化,完成选项卡的操作
init:function(){
//=>6、获取当前页卡区域中的元素(LI & DIV) 把获取到的值都通过this挂载到这个实例上去,以后再其他方法中也能用
//=>直接通过this写到实例上
this.tab = utils.children(this.tabBox, 'ul')[0],
this.tabList = utils.children(this.tab, 'li'),
this.conList = utils.children(this.tabBox, 'div'),
this.prevIndex = 0;
// =>7、实现页卡切换
this.change();
}
};
window.CT = ChangeTab;
}();
var tabBoxList = utils.getElementsByClassName('tabBox');
for (var i = 0; i < tabBoxList.length; i++) {
new CT(tabBoxList[i]);
}
完善插件,options额外配置项,通过options完成很多其他功能
~function(){
function ChangeTab(tabBox,options) {//1、构造函数创建一个ChangeTab类
//=>8、参数初始化
var _default = {
initIndex:0,
eventType:'click'
};
//=>9、循环遍历替换默认_default参数值
for(var key in options){
if(options.hasOwnProperty(key)){
_default[key] = options[key];
}
}
//10、把默认值放到实例上,这些值在类的任何方法中都可以调取使用
this.initIndex = _default.initIndex;
this.eventType = _default.eventType;
//=>3、技巧:为了保证这些值在类的任何方法中都可以调取使用,我们一般都把信息值存放在当前实例上(THIS):只要保证每个方法中的THIS都是当前类的实例,我们就可以获取这些值了
// 构造函数封装的时候,我们一般会把常用的公共用的东西写在this上(也就是当前实例上)
this.tabBox = tabBox;
//=>5、开始实现选项卡的功能
// ChangeTab.prototype.init().call(this);
this.init();
}
ChangeTab.prototype = {//2、往类的原型上写方法,(重构自定义类的原型,需要保证constructor: ChangeTab)
constructor: ChangeTab,
//7、实现页卡切换
change:function(){
// 实际项目中开发写法如下:不定义变量,都通过this直接操作
var _this = this;
for (var i = 0; i < _this.tabList.length; i++) {
_this.tabList[i].myIndex = i;
//13、事件
// _this.tabList[i].onclick = function () {
_this.tabList[i]['on' + _this.eventType] = function () {
//=>this:当前点击的这个LI,不是实例(_this是实例)
_this.tabList[_this.prevIndex].className = '';
_this.conList[_this.prevIndex].className = 'con';
this.className = 'select';
_this.conList[this.myIndex].className = 'con select';
_this.prevIndex = this.myIndex;
}
}
},
//=>11、清空所有的样式类
clear:function(){
for(var i = 0; i < this.tabList.length; i++){
this.tabList[i].className = '';
this.conList[i].className = 'con';
}
//=>初始化默认的选中页卡
this.tabList[this.initIndex].className = 'select';
this.conList[this.initIndex].className='con select';
},
//4 init初始化,完成选项卡的操作
init:function(){
//=>6、获取当前页卡区域中的元素(LI & DIV) 把获取到的值都通过this挂载到这个实例上去,以后再其他方法中也能用
//=>直接通过this写到实例上
this.tab = utils.children(this.tabBox, 'ul')[0],
this.tabList = utils.children(this.tab, 'li'),
this.conList = utils.children(this.tabBox, 'div'),
this.prevIndex = 0;
//12、清空所有的样式类
this.clear();
// =>7、实现页卡切换
this.change();
}
};
window.CT = ChangeTab;
}();
var tabBoxList = utils.getElementsByClassName('tabBox');
// for (var i = 0; i < tabBoxList.length; i++) {
// new CT(tabBoxList[i],{
// initIndex:1,
// eventType:'mouseover'
// });
// }
new CT(tabBoxList[0]);
new CT(tabBoxList[1], {
eventType: 'mouseover'
});
var aa = new CT(tabBoxList[2], {
eventType: 'mouseover',
initIndex: 2
});
console.log(aa);
// 插件必须构造函数来写
// 所有信息之间的传输都是基于实例this来实现的,所有的方法都写在原型上,所有要使用的值都存在实例的私有属性上
// 插件封装不是把功能实现就可以了,而是尽可能提供更多的可变性,更多的可变性通过传参options来一步步实现,封装插件可以保证独立性
// 如果有额外的需求可以去扩展,然后通过实例调用这个方法
//=>插件:iscroll swiper
//=>组件:UI组件 bootstrap
//=>类库:jQuery zepto
//=>框架:vue react