原始代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#my-slider{
position: relative;
width: 790px;
height: 400px;
}
.slider-list ul{
list-style-type: none;
position: relative;
padding: 0;
margin: 0;
}
.slider-list_item,
.slider-list_item--selected{
position: absolute;
transition: opacity 1s;
opacity: 0;
text-align: center;
}
.slider-list_item--selected{
transition: opacity 1s;
opacity: 1;
}
</style>
</head>
<body>
<div id="my-slider" class="slider-list">
<ul>
<li class="slider-list_item--selected">
<img src="../素材库/ab97fd983f935901892202a7c8358345.jpeg" alt="">
</li>
<li class="slider-list_item">
<img src="../素材库/b2d3e58811b2b0054d440c87e60b3a45.jpeg" alt="">
</li>
<li class="slider-list_item">
<img src="../素材库/cdc625752f4fc2a84c9a94abf9a4685b.jpeg" alt="">
</li>
<li class="slider-list_item">
<img src="../素材库/ab97fd983f935901892202a7c8358345.jpeg" alt="">
</li>
</ul>
<a class="slider-list_next"></a>
<a class="slider-list_previous"></a>
<div class="slide-list_control">
<span class="slide-list_control-buttons--selected"></span>
<span class="slide-list_control-buttons"></span>
<span class="slide-list_control-buttons"></span>
<span class="slide-list_control-buttons"></span>
</div>
</div>
<script>
class Slider{
constructor(id,cycle = 3000){
// 获取容器
this.container = document.getElementById(id);
// 获取所有图片
this.items = this.container.querySelectorAll('.slider-list_item,.slider-list_item--selected')
this.cycle = cycle
// 获取底部控制器
const controller = this.container.querySelector('.slide-list_control')
if(controller){
// 获取所有圆形按钮
const buttons = controller.querySelectorAll('.slide-list_control-buttons,.slide-list_control-buttons--selected')
controller.addEventListener('mouseover',e=>{
// 获取被选中的按钮索引
const idx = Array.from(buttons).indexOf(e.target)
if(idx >= 0){
this.slideTo(idx)
this.stop()
}
})
controller.addEventListener('mouseout',e=>{
this.start()
})
// 绑定自定义slide事件
this.container.addEventListener('slide',e=>{
const idx = e.detail.index
const selected = controller.querySelector('.slide-list_control-buttons--selected')
if(selected) selected.className = 'slide-list_control-buttons'
buttons[idx].className = 'slide-list_control-buttons--selected'
})
}
const previous = this.container.querySelector('.slide-list_previous')
if(previous){
previous.addEventListener('click',e=>{
this.stop()
this.slidePrevious()
this.start()
e.preventDefault()
})
}
const next = this.container.querySelector('.slide-list_next')
if(next){
next.addEventListener('click',e=>{
this.stop()
this.slideNext()
this.start()
e.preventDefault()
})
}
}
getSelectedItem(){
const selected = this.container.querySelector('.slider-list_item--selected')
return selected
}
getSelectedItemIndex(){
return Array.from(this.items).indexOf(this.getSelectedItem())
}
slideTo(idx){
const selected = this.getSelectedItem()
if(selected){
selected.className = 'slider-list_item'
}
const item = this.items[idx]
if(item){
item.className = 'slider-list_item--selected'
}
const detail = {index:idx}
const event = new CustomEvent('slide',{bubbles:true,detail})
this.container.dispatchEvent(event)
}
slideNext(){
const currentIdx = this.getSelectedItemIndex()
const nextIdx = (currentIdx+1)% this.items.length
this.slideTo(nextIdx)
}
slidePrevious(){
const currentIdx = this.getSelectedItemIndex()
const previousIdx = (this.items.length + currentIdx - 1)%this.items.length
this.slideTo(previousIdx)
}
start(){
this.stop()
this.timer = setInterval(()=>{
this.slideNext()
},this.cycle)
}
stop(){
clearInterval(this.timer)
}
}
const slider = new Slider('my-slider')
slider.start()
</script>
</body>
</html>
该代码定义一个Slider类,实现轮播图的功能
- 在构造函数中给容器,控制器与其按钮,左右箭头绑定事件监听器
- 在类中创建各方法
- new创建实例对象
- 代码的缺点:
- 可维护性差,如果要修改某个功能,需要同时对html,css,js做相应的修改
- 构造函数过于臃肿
第一次改进,插件化
将控制元素抽取成插件,插件与组件之间通过依赖注入的方式建立联系
constructor(id,cycle = 3000){
this.container = document.getElementById(id);
this.items = this.container.querySelectorAll('.slider-list_item,.slider-list_item--selected')
this.cycle = cycle
}
registerPlugins(...plugins){
// 将this注入
plugins.forEach(plugin => plugin(this))
}
function pluginController(slider){
const controller = slider.container.querySelector('.slide-list_control')
if(controller){
const buttons = controller.querySelectorAll('.slide-list_control-buttons,.slide-list_control-buttons--selected')
controller.addEventListener('mouseover',e=>{
const idx = Array.from(buttons).indexOf(e.target)
if(idx >= 0){
slider.slideTo(idx)
slider.stop()
}
})
controller.addEventListener('mouseout',e=>{
slider.start()
})
slider.container.addEventListener('slide',e=>{
const idx = e.detail.index
const selected = controller.querySelector('.slide-list_control-buttons--selected')
if(selected) selected.className = 'slide-list_control-buttons'
buttons[idx].className = 'slide-list_control-buttons--selected'
})
}
}
function pluginPrevious(slider){
const previous = slider.container.querySelector('.slide-list_previous')
if(previous){
previous.addEventListener('click',e=>{
slider.stop()
slider.slidePrevious()
slider.start()
e.preventDefault()
})
}
}
function pluginNext(slider){
const next = slider.container.querySelector('.slide-list_next')
if(next){
next.addEventListener('click',e=>{
slider.stop()
slider.slideNext()
slider.start()
e.preventDefault()
})
}
}
将轮播器和底部控制器,左右箭头,抽取成插件,使用注册插件依赖注入的形式控制
slider.registerPlugins(pluginController,pluginPrevious,pluginNext)
-
优点: 构造函数变得简洁,不需要某个功能时,只需要把那个插件取消注册即可
-
仍有缺点: 对于html部分仍需要去页面中删除
第二次改进,模板化
html
<div id="my-slider" class="slider-list"></div>
js
constructor(id,opts= {images:[],cycle :3000}){
this.container = document.getElementById(id);
this.options = opts
this.container.innerHTML = this.render()
this.items = this.container.querySelectorAll('.slider-list_item,.slider-list_item--selected')
this.cycle = cycle
this.slideTo(0)
}
render(){
const images = this.options.images
const content = images.map(image =>
` <li class="slider-list_item--selected">
<img width="790" height="400" src="${image}" alt="">
</li>`.trim()
)
return `<ul>${content.join('')}</ul>`
}
registerPlugins(...plugins){
// 将this注入
plugins.forEach(plugin => {
const pluginContainer = document.createElement('div')
pluginContainer.className = '.slider-list_plugin'
pluginContainer.innerHTML = plugin.render(this.options.images)
this.container.appendChild(pluginContainer)
plugin.action(this)
} )
}
const pluginController={
render(images){
return
`
<div class="slide-list_control">
${images.map((image,i)=>
`
<span class="slide-list_control-buttons${i===0?'--selected':''}"></span>
`
).join('')}
</div>
`.trim()
},
action(Slider){
const controller = slider.container.querySelector('.slide-list_control')
if(controller){
const buttons = controller.querySelectorAll('.slide-list_control-buttons,.slide-list_control-buttons--selected')
controller.addEventListener('mouseover',e=>{
const idx = Array.from(buttons).indexOf(e.target)
if(idx >= 0){
slider.slideTo(idx)
slider.stop()
}
})
controller.addEventListener('mouseout',e=>{
slider.start()
})
slider.container.addEventListener('slide',e=>{
const idx = e.detail.index
const selected = controller.querySelector('.slide-list_control-buttons--selected')
if(selected) selected.className = 'slide-list_control-buttons'
buttons[idx].className = 'slide-list_control-buttons--selected'
})
}
}
}
const pluginPrevious={
render(){
return `<a class="slider-list_previous"></a>`
},
action(slider){
const previous = slider.container.querySelector('.slide-list_previous')
if(previous){
previous.addEventListener('click',e=>{
slider.stop()
slider.slidePrevious()
slider.start()
e.preventDefault()
})
}
}
}
const pluginNext={
render(){
return `<a class="slider-list_next"></a>`
},
action(slider){
const next = slider.container.querySelector('.slide-list_next')
if(next){
next.addEventListener('click',e=>{
slider.stop()
slider.slideNext()
slider.start()
e.preventDefault()
})
}
}
}
const slider = new Slider('my-slider',{
// 图片库由此导入
images:[]
,cycle:3000
}
通过将html模板化,使得HTML代码优化仅剩一行,而剩余的html代码由各插件模板创建
- 优点: 将html模板化,更易于扩展
最终封装,组件框架
class Component{
constructor(id,opts = {className,images:[]}){
this.container = document.getElementById(id)
this.options = opts
this.container.innerHTML = this.render(opts.images)
}
registerPlugins(...plugins){
// 将this注入
plugins.forEach(plugin => {
const pluginContainer = document.createElement('div')
pluginContainer.className = className
pluginContainer.innerHTML = plugin.render(this.options.images)
this.container.appendChild(pluginContainer)
plugin.action(this)
} )
}
// 类似虚函数
render(images){
return ''
}
}
class Slider extends Component{
constructor(id,opts= {className:'slider-list_plugin',images:[],cycle :3000}){
// super 调用父类的构造方法
super(id,opts)
this.items = this.container.querySelectorAll('.slider-list_item,.slider-list_item--selected')
this.cycle = cycle
this.slideTo(0)
}
// 重写render方法
render(images){
const content = images.map(image =>
` <li class="slider-list_item--selected">
<img width="790" height="400" src="${image}" alt="">
</li>`.trim()
)
return `<ul>${content.join('')}</ul>`
}
}
-
创建模板类,包含构造函数,注册插件方法,渲染方法
-
创建slider类继承模板类,在构造函数中调用父类的构造方法,初始化自己的变量,并重写父类的render方法