用jQuery写个轮播

618 阅读6分钟

1. 前端指导思想:

内容(HTML)、样式(CSS)与行为(JS)分离

  • 如果用HTML写样式,会让内容很冗长复杂,会有很多多余的标签,很难区分逻辑结构。
  • 如果用CSS表示内容,一是用户无法选中该内容,二是JS无法取不到这个内容。
  • 如果用JS控制内容,比如假设CSS div{display: none}$div.hide() // display: none,这个是没有争议的,但如果是$div.show() // display: block or inline-felx,这种不确定性会影响页面布局。
    我们应该只用JS加class,具体的样式CSS自己搞定。
  • 在实际应用中会迫不得已地违反此标准,但如果经常违反,那你就没有遵循这个标准,你写的代码将会混乱不堪。

2. 轮播

记录我写一个简单的轮播效果的过程。

实际操作步骤:
一:让图片横起来,且只显示一个。

  1. 找三个图片放上去,设置宽300PX,前端是不会让图片变形的,所以要不找三个大小一样的,要不就自己P图。我是找了三个300*200的。
  2. 用一个窗口包裹imgs。
  3. 让三个图片横起来:display: felx;,但是flex默认会尽量让所有东西放在一行里。
  4. 所以再加一句align-items: flex-start;这样就可以了。具体原理自己理解,或者可以强行记忆。
  5. 给window和imgs加边框,因为屏幕不够宽,所有没有包裹住所有图片。最右边的图片会超过边际。
  6. 这可以写一个overflow: hidden;遮挡住超出边际的部分,本次不需要这样。
  7. 给window加一个宽度width: 300px; overflow: hidden;这样一次就只显示一个图片。

二:实现轮播

  1. 加三个按钮,点击不同的按钮出现不同的图片。
  2. 在js里用jQuery给三个按钮分别添加onclick函数,点击就改版translateX的值。
  3. 加个过渡:因为动的是images,所以在miges里加上transition: transform 0.5s;

代码如下:

html:
<!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;
      border: 5px solid red;
      transition: transform 0.5s;
    }
    .iamges > img{
        vertical-align: top;
    }
    .window{
      border: 10px solid black;
      width: 300px;
      overflow: hidden;
    }
  </style>
</head>
<body>
  
  <div class="window">
<div class="images" id=images>
  <img src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=4137085231,3132721994&fm=15&gp=0.jpg" width=300 height=200 alt="">
  <img src="https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1192434636,812615715&fm=15&gp=0.jpg" width=300 height=200 alt="">
  <img src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1683434367,3791017001&fm=15&gp=0.jpg" width=300 height=200 alt="">
</div>
</div>
  
  <button id="p1">第1张</button>
  <button id="p2">第2张</button>
  <button id="p3">第3张</button>
</body>
</html>
JS:
$(p1).on('click',function(){
  $(images).css({
    transform: 'translateX(0)'
  })
})
$(p2).on('click',function(){
  $(images).css({
    transform: 'translateX(-300px)'
  })
})
$(p3).on('click',function(){
  $(images).css({
    transform: 'translateX(-600px)'
  })
})
  1. 为什么布局用flex,而不用float?
    因为用flex写的代码少(2句),float代码多(4句)。
  2. 为什么用$(images).css,而不是xxx.style
    因为jQuery就是这样设计的。.css去改版style,这是个设计问题,现在没法改了,只能用这。
    3. 为什么要用$(p1).on('click',function(){...})这样的写法,而不是用xxx.onclick?
    因为jQuery推荐这样的写法,也好记。第一个参数是什么事件,第二个参数是事件触发要执行什么函数。
    $(p1).on('click',function(){}) === $(p1).onclick(function(){}),后者是简便写法。
  • 知识点: .on:先把一个元素封装一下得到一个新的对象,这个对象提供了.on的API,它接受两个参数,第一个是事件名,第二个是时间触发时要执行的函数。这个函数也是有一个参数的,这个参数记录了事件的所有信息。
  1. 不是说了要遵守规则吗,怎么还在img上面写了宽度?
    因为img是可替换元素,页面没有打开的时候是不知道图片的大小的,浏览器先用一个小的图片占位,等到下载图片的时候要给图片让位置,很浪费性能,所以如果我们已经知道了图片的宽高,我们最好是直接写上去,这样浏览器就少一次让后面图片让位的步骤。
  2. 不是说了要遵守规则吗,怎么还用JS控制CSS呢?
    如果分开写,要写很多代码,每一张图片都要在CSS里写个样式,JS要添加class并删除别的图片添加的class,这次只有三个,如过有100张呢?所以迫不得已,只能打破这个原则了。

这是一个新手应该想到的轮播,但是有很多不足的地方。

  1. 仔细观看图片切换,能看出来切换的过程中图片会轻微的蹦一下!这是CSS的问题
  • 排查问题,通过各种删除,比如先删除图片,把button改成span等来找出问题的地方。我们发现,是images的问题。我们只改变了images的transform,然后给images设置宽高还是出问题,这更加确定是transform的问题,所以我们就不用它了,改用margin-left。
  • 再排查,发现是因为页面放大110%的问题,改回100%就可以了,emmm,CSS就是这么蛋疼。

优化JS

JS部分发现代码都比较相似,所以有优化的可能。

  1. 用span包裹三个button,用for循环监听用户点击的是哪个按钮
  2. 使用$().index()找到用户点击的图片是第几个。
  3. 根据用户点击的按钮添加CSS
var allButtons = $('#buttons > button')

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)'
    })
  })
}

自动轮播

如果让三张图片自动轮播呢?我们只需要循环点击三个按钮即可。

  1. 设置一个n,随时间增加而增加,如果是三张图,n%3得到的就是0 1 2 0 1 2 ...
  2. 获取到一个元素的第n%3个儿子:使用.eq()
    知识点:$(a).eq(b)会找到找出对应的dom并封装成一个jq对象。
    • dom :a[b].xxx
    • .eq : a.eq(b).xxx
  3. 点击
    知识点:可以直接用$(a).eq(b).click,也可以用$(a).eq(b).trigger('click'),推荐使用trigger,因为它功能更强大,可以接任何事件的名字。
  4. 使用setInterval()控制循环间隔。

代码如下:

var n = 0;
setInterval( ()=> {
  n += 1
  allButtons.eq(n%3).trigger('click')
},1000)

优化

  1. 有几张图片是不固定的,不能写死,所以用一个变量代替。
    var size = allButtons.length
  2. 播到哪张图片,就让那个图片的按钮变红。
css:
.red{
    color: red;
}

JS:
var n = 0;
var size = allButtons.length;
setInterval( ()=> {
  n += 1
  allButtons.eq(n%size).trigger('click')
  .addClass('red')
  .siblings('.red').removeClass('red')
},1000)
  1. 鼠标悬停到图片上停止轮播,离开继续轮播

注意,监听的是window,而不是images

JS:
$('.window').on('mouseenter', function(){
  window.clearInterval(timeId)
})
$('.window').on('mouseleave', function(){
  timeId = setInterval( ()=> {
  n += 1
  allButtons.eq(n%size).trigger('click')
  .addClass('red')
  .siblings('.red').removeClass('red')
},1000)
})

  1. 发现一个小bug:我想点按钮,从我点的按钮处继续轮播
    原本轮播就是按照n的值一直轮播,跟我点的index没有关系,那我让n=index,这样就可以了。
...
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  
    allButtons.eq(n)
      .addClass('red')
      .siblings('.red').removeClass('red')
  })
}
...
  1. 重复的代码很多,优化一下
var allButtons = $('#buttons > button')

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
    avtiveButton(allButtons.eq(n))
  })
}

var n = 0;
var size = allButtons.length;
playSlide(n%size)

var timeId = setTimer()

$('.window').on('mouseenter', function(){
  window.clearInterval(timeId)
})
$('.window').on('mouseleave', function(){
  timeId = setTimer()
})

function avtiveButton($button){
  $button
  .addClass('red')
  .siblings('.red').removeClass('red')
}
function playSlide(index){
    allButtons.eq(index).trigger('click')
}
function setTimer(){
    return setInterval( ()=> {
    n += 1
    playSlide(n%size)  
  },3000)
}