代码:https://github.com/Mcguffen/jQuery-demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > </head > <body > <ul > <li id ="item1" > 选项1</li > <li id ="item2" > 选项2</li > <li id ="item3" > 选项3</li > <li id ="item4" > 选项4</li > <li id ="item5" > 选项5</li > </ul > </body > </html >
封装函数 1 function getSiblings(node){}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function getSiblings (node ) {var allChildren = node.parentNode.children var array = { length : 0 }for (let i = 0 ; i < allChildren.length; i++){ if (allChildren[i] !== item3){ array[array.length] = allChildren[i] array.length += 1 } } return array} console .log(getSiblings(item3));
再封装一个
1 function addClass(node, classes){}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function addClasses (node,classes ) {var classes = { 'a' : true , 'b' :false , 'c' :true } for (let key in classes){ var value = classes[key] if (value){ node.classList.add(key); }else { node.classList.remove(key) } } }
function addClasses(node, classes)代码优化
1 2 3 4 5 6 7 8 9 10 11 12 function addClasses (node, classes ) { for (let key in classes) { var value = classes[key] var methodName = value ? 'add' : 'remove' node.clasList[methodName](key) } } addClasses(item1, { 'a' : true ,'b' : true , 'c' : true }
命名空间 为了让上面两个方法有关联性 因为这两个方法都是操作node 这种我认为好用的套路 也想让别人用 就用命名空间 命名后给别人用 我们就命名为ydom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 window .ydom = {}ydom.getSiblings= function (node ) { var allChildren = node.parentNode.children var array = { length : 0 } for (let i = 0 ; i < allChildren.length; i++) { if (allChildren[i] !== node) { array[array.length] = allChildren[i] array.length += 1 } } return array } ydom.addClasses =function addClasses (node, classes ) { for (let key in classes) { var value = classes[key] var methodName = value ? 'add' : 'remove' node.classList[methodName](key) } } ydom.getSiblings(item2) console .log(ydom.getSiblings(item2));ydom.addClasses(item2,{'a' :false ,'b' :true ,'c' :true }) console .log(item2);
能不能把 node 放前面 1 2 3 node.getSiblings() node.addClass()
方法一:扩展 Node 接口 直接在 Node.prototype 上加函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // 上面的问题是不好用 每次还要ydom.xx()每次都要ydom // ydom.getSiblings(item2) // 我想 item2.getSiblings() // 原型链 // 给Node的共有属性添加一个getSibs()的方法 那么作为节点item2肯定有这个节点。 Node.prototype.getSiblings = function(){ // 将目标节点的兄弟节点放入一个数组 // node 就是this this就是谁调用就是谁 var allChildren = this.parentNode.children // 伪数组 一个哈希 没有push()方法 // 理解array[array.length] var array = { length: 0 } for (let i = 0; i < allChildren.length; i++) { if (allChildren[i] !== this) { array[array.length] = allChildren[i] array.length += 1 } } return array } item2.getSiblings() console.log(item2.getSiblings()); console.dir(item2); // 原型链 // 给Node的共有属性添加一个getSibs()的方法 那么作为节点item2肯定有这个节点。 Node.prototype.addClasses = function(classes){ // 为了又可以添加class又能remove class true为添加 false为不添加 for (let key in classes) { var value = classes[key] var methodName = value ? 'add' : 'remove' this.classList[methodName](key) } } item2.addClasses({'a':true,"b":false,"c":false}) console.log(item2);
方法二:新的接口 BetterNode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function Node2(node){ return { element: node, getSiblings: function(){ }, addClass: function(){ } } } let node =document.getElementById('x') let node2 = Node2(node) node2.getSiblings() node2.addClass()
上面修改原型链的方法也不妥 现在问题是 别人也可以重修这个共有属性 大家相互覆盖 那怎么办呢? 上面大家都修改同一个Node的原型链 那么 我们新创建一个名叫别的Node不就行了? 至于叫什么 叫啥都行 就反正不冲突就行 你叫jQuery都行。。。 比如叫Yode行不行 我们自己弄一个构造函数 你给我一个节点我返回给你一个新的节点,新节点带有2个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 window .Yode = function (node ) { return { getSiblings: function ( ) { var allChildren = node.parentNode.children var array = { length : 0 } for (let i = 0 ; i < allChildren.length; i++) { if (allChildren[i] !== node) { array[array.length] = allChildren[i] array.length += 1 } } return array }, addClasses: function (classes ) { for (let key in classes) { var value = classes[key] var methodName = value ? 'add' : 'remove' node.classList[methodName](key) } } } } var yode1 = Yode(item1) yode1.getSiblings() console .log(yode1); console .log(yode1.getSiblings() ); yode1.addClasses({'a' :true ,'b' :false ,'c' :false })
第二种叫做「无侵入」。
把 Node2 改个名字吧 大胆点就叫jQuery 名字不重要 上面我们都叫Yode了 你也可以叫jQuery 比如下面这样 反正核心是 通过Yode这个构造函数得到一个新的节点 这个节点带有构造函数的两个方法给你用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function jQuery(node){ return { element: node, getSiblings: function(){ }, addClass: function(){ } } } let node =document.getElementById('x') let node2 =jQuery(node) node2.getSiblings() node2.addClass()
所以叫啥都行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 window .jQuery = function (node ) { return { getSiblings: function ( ) { var allChildren = node.parentNode.children var array = { length : 0 } for (let i = 0 ; i < allChildren.length; i++) { if (allChildren[i] !== node) { array[array.length] = allChildren[i] array.length += 1 } } return array }, addClasses: function (classes ) { for (let key in classes) { var value = classes[key] var methodName = value ? 'add' : 'remove' node.classList[methodName](key) } } } } var yode1 = jQuery(item1) yode1.getSiblings() console .log(yode1); console .log(yode1.getSiblings() ); yode1.addClasses({'a' :true ,'b' :false ,'c' :false })
再给个缩写吧 alias 比如window.$ = jQuery
**改进零:接受字符串是选择器
那么jQuery更厉害一点 参数不局限于节点也可以接受一个字符串,这个字符串可以是一个选择器 我们改进一下参数接受节点或者选择器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 window .jQuery = function (nodeOrSelector ) { if (typeof nodeOrSelector === 'string' ){ node = document .querySelector(nodeOrSelector) console .log(node); }else { node = nodeOrSelector } return { getSiblings: function ( ) { var allChildren = node.parentNode.children var array = { length : 0 } for (let i = 0 ; i < allChildren.length; i++) { if (allChildren[i] !== node) { array[array.length] = allChildren[i] array.length += 1 } } return array }, addClasses: function (classes ) { for (let key in classes) { var value = classes[key] var methodName = value ? 'add' : 'remove' node.classList[methodName](key) } } } } window .$ = jQueryvar yode2 = $('#item2' )yode2.getSiblings() console .log(yode2); console .log(yode2.getSiblings());yode2.addClasses({'a' :true ,'b' :false ,'c' :false })
核心就是这个 判断一下传的是字符串选择器还是节点
1 2 3 4 5 6 if (typeof nodeOrSelector === 'string' ){ node = document .querySelector(nodeOrSelector) console .log(node); }else { node = nodeOrSelector }
改进一:改掉 document.getElementById
改进二:接受多个 node
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 window .jQuery = function (nodeOrSelector ) { let nodes = {} if (typeof nodeOrSelector === 'string' ){ let temp = document .querySelectorAll(nodeOrSelector) for (let i=0 ;i<temp.length;i++){ nodes[i] = temp[i] } nodes.length = temp.length }else if (nodeOrSelector instanceof Node){ nodes = { 0 : nodeOrSelector, length: 1 } } nodes.getSiblings = function ( ) { } nodes.addClasses = function (classes ) { for (let key in classes) { var value = classes[key] var methodName = value ? 'add' : 'remove' for (let i = 0 ; i<nodes.length; i++){ nodes[i].classList[methodName](key) } } } return nodes } window .$ = jQueryvar yodeAll = $('ul>li' )yodeAll.addClasses({'a' :true ,'b' :false ,'c' :false }) console .log(yodeAll);
改进三:添加 .html() .text() .css() 等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 window .jQuery = function (nodeOrSelector ) { let nodes = {} if (typeof nodeOrSelector === 'string' ){ let temp = document .querySelectorAll(nodeOrSelector) for (let i=0 ;i<temp.length;i++){ nodes[i] = temp[i] } nodes.length = temp.length }else if (nodeOrSelector instanceof Node){ nodes = { 0 : nodeOrSelector, length: 1 } } nodes.getSiblings = function ( ) { } nodes.addClasses = function (classes ) { for (let key in classes) { var value = classes[key] var methodName = value ? 'add' : 'remove' for (let i = 0 ; i<nodes.length; i++){ nodes[i].classList[methodName](key) } } } nodes.getAllTexts = function ( ) { let texts = [] for (let i = 0 ; i<nodes.length; i++){ texts.push(nodes[i].textContent) } return texts } nodes.setAllTexts = function (text ) { let texts = [] for (let i = 0 ; i<nodes.length; i++){ nodes[i].textContent = text } return texts } return nodes } window .$ = jQueryvar yodeAll = $('ul>li' )yodeAll.addClasses({'a' :true ,'b' :false ,'c' :false }) console .log(yodeAll);console .log(yodeAll.getAllTexts())yodeAll.setAllTexts('hello' )
优化 合并上面获取和修改全部节点文本内容的方法 不给参数就是获取全部文本内容 给参数就是修改全部文本内容 jQuery有大量api就是这么设计的,给参数就是修改,不给就是获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 window .jQuery = function (nodeOrSelector ) { let nodes = {} if (typeof nodeOrSelector === 'string' ){ let temp = document .querySelectorAll(nodeOrSelector) for (let i=0 ;i<temp.length;i++){ nodes[i] = temp[i] } nodes.length = temp.length }else if (nodeOrSelector instanceof Node){ nodes = { 0 : nodeOrSelector, length: 1 } } nodes.getSiblings = function ( ) { } nodes.addClasses = function (classes ) { for (let key in classes) { var value = classes[key] var methodName = value ? 'add' : 'remove' for (let i = 0 ; i<nodes.length; i++){ nodes[i].classList[methodName](key) } } } nodes.text = function (text ) { if (text === undefined ){ let texts = [] for (let i = 0 ; i<nodes.length; i++){ texts.push(nodes[i].textContent) } return texts }else { let texts = [] for (let i = 0 ; i<nodes.length; i++){ nodes[i].textContent = text } return texts } } return nodes } window .$ = jQueryvar yodeAll = $('ul>li' )yodeAll.addClasses({'a' :true ,'b' :false ,'c' :false }) console .log(yodeAll);yodeAll.text() console .log(yodeAll.text());yodeAll.text('hi' )
JQuery 优点
jQuery 在兼容性方面做得很好,1.7 版本兼容到 IE 6
jQuery 还有动画、AJAX 等模块,不止 DOM 操作
jQuery 的功能更丰富
jQuery 使用了 prototype,我们没有使用,等讲了 new 之后再用
JQuery 实现一个轮播
HTML、CSS、JS 内容、样式与行为分离
轮播的思路
i. 思路1:滚来滚去
ii. 思路2:用局部画面骗用户
封装思路
i. 从 API 开始思考
ii. 尽量能让使用者猜到
代码:
重构前:
html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html> <html> <head> <script src="//code.jquery.com/jquery-2.1.1.min.js"></script> <meta charset="utf-8"> <title>JS Bin</title> <style> .images{ display: flex; align-items: flex-start; transition: all 0.5s; } .images > img{ vertical-align: top; } .window{ width: 300px; overflow: hidden; } </style> </head> <body> <div class="window"> <div class="images" id=images> <img src="https://fthmb.tqn.com/0ui_Zw01Ht9NHJkSBlqOIC1IH44=/960x0/filters:no_upscale()/yorkshire-terrier-583788122-581630e85f9b581c0b018a00.jpg" width=300 alt="图片1" height=200> <img src="https://canna-pet.com/wp-content/uploads/2017/06/20-longest-living-dog-breeds_canna-pet-e1498599846169.jpg" width=300 alt="" height=200> <img src="https://fthmb.tqn.com/0ui_Zw01Ht9NHJkSBlqOIC1IH44=/960x0/filters:no_upscale()/yorkshire-terrier-583788122-581630e85f9b581c0b018a00.jpg" width=300 alt="" height=200> <img src="https://canna-pet.com/wp-content/uploads/2017/06/20-longest-living-dog-breeds_canna-pet-e1498599846169.jpg" width=300 alt="" height=200> </div> </div> <span id=buttons> <span>第1张</span> <span>第2张</span> <span>第3张</span> <span>第4张</span> </span> </body> </html>
js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 var allButtons = $('#buttons > span') for (let i = 0; i < allButtons.length; i++) { $(allButtons[i]).on('click', function(x) { console.log('hi') var index = $(x.currentTarget).index() var p = index * -300 $('#images').css({ transform: 'translate(' + p + 'px)' }) n = index allButtons.eq(n) .addClass('red') .siblings('.red').removeClass('red') }) } var n = 0; var size = allButtons.length allButtons.eq(n % size).trigger('click') .addClass('red') .siblings('.red').removeClass('red') var timerId = setInterval(() => { n += 1 allButtons.eq(n % size).trigger('click') .addClass('red') .siblings('.red').removeClass('red') }, 1000) $('.window').on('mouseenter', function() { window.clearInterval(timerId) }) $('.window').on('mouseleave', function() { timerId = setInterval(() => { n += 1 allButtons.eq(n % size).trigger('click') .addClass('red') .siblings('.red').removeClass('red') }, 3000) })
重构后:
html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!DOCTYPE html > <html > <head > <script src ="//code.jquery.com/jquery-2.1.1.min.js" > </script > <meta charset ="utf-8" > <title > JS Bin</title > <style > .images{ display : flex; align-items : flex-start; transition : all 0.5s ; } .images > img { vertical-align : top; } .window{ width : 300px ; overflow : hidden; } </style > </head > <body > <div class ="window" > <div class ="images" id =images > <img src ="https://fthmb.tqn.com/0ui_Zw01Ht9NHJkSBlqOIC1IH44=/960x0/filters:no_upscale()/yorkshire-terrier-583788122-581630e85f9b581c0b018a00.jpg" width =300 alt ="图片1" height =200 > <img src ="https://canna-pet.com/wp-content/uploads/2017/06/20-longest-living-dog-breeds_canna-pet-e1498599846169.jpg" width =300 alt ="" height =200 > <img src ="https://fthmb.tqn.com/0ui_Zw01Ht9NHJkSBlqOIC1IH44=/960x0/filters:no_upscale()/yorkshire-terrier-583788122-581630e85f9b581c0b018a00.jpg" width =300 alt ="" height =200 > <img src ="https://canna-pet.com/wp-content/uploads/2017/06/20-longest-living-dog-breeds_canna-pet-e1498599846169.jpg" width =300 alt ="" height =200 > </div > </div > <span id =buttons > <span > 第1张</span > <span > 第2张</span > <span > 第3张</span > <span > 第4张</span > </span > </body > </html >
js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 var allButtons = $('#buttons > span' )for (let i = 0 ; i < allButtons.length; i++) { $(allButtons[i]).on('click' , function (x ) { var index = $(x.currentTarget).index() var p = index * -300 $('#images' ).css({ transform: 'translate(' + p + 'px)' }) n = index activeButton(allButtons.eq(n)) }) } var n = 0 ;var size = allButtons.lengthallButtons.eq(n % size).trigger('click' ) var timerId = setTimer()function setTimer ( ) { return setInterval (() => { n += 1 allButtons.eq(n % size).trigger('click' ) }, 3000 ) } function activeButton ($button ) { $button .addClass('red' ) .siblings('.red' ).removeClass('red' ) } $('.window' ).on('mouseenter' , function ( ) { window .clearInterval(timerId) }) $('.window' ).on('mouseleave' , function ( ) { timerId = setTimer() })