各司其职
JS 控制页面行为,CSS 控制页面呈现,HTML 控制页面结构
背景变化
<body class="">
<h1> Hello World </h1>
<button id="backBtn"> changeBack </button>
</body>
<script>
const btn = document.getElementById('backBtn')
btn.addEventListener('click', () => {
const body = document.body;
if (body.style.backgroundColor === 'white') {
body.style.backgroundColor = 'black';
body.style.color = 'white';
} else {
body.style.backgroundColor = 'white';
body.style.color = 'black';
}
})
</script>
<style>
body {
background-color: white;
}
</style>
在这个版本中我们看到页面的颜色变化完全是通过 js 来控制的,通过给 btn 绑定监听事件,通过获取到 body.style 来控制页面背景呈现
<script>
const btn = document.getElementById('backBtn')
btn.addEventListener('click', () => {
const body = document.body;
if (body.className === '') {
body.className = 'black';
} else {
body.className = '';
}
})
</script>
<style>
body {
background-color: white;
color: black;
}
.black {
background-color: black;
color: white;
}
</style>
可以看到通过 js 来控制 body 的 class,通过 css 来控制背景颜色的变化让我们的 js 代码部分看起来更加清晰,也让 js 和 css 的职能更好地被区别出来。当然我们可能会遇到操作对象不是 button 的情况,这时候我们可以通过一个 checkBox 配合 for 属性来实现。
<body>
<input id="backBtn" type="checkbox" />
<div class="content">
<label for="backBtn"> Hello World </label>
</div>
</body>
<style>
#backBtn:checked+.content {
background-color: black;
color: white;
transition: all 1s;
}
#backBtn {
display: none;
}
</style>
通过上述三个版本我们可以发现在通过每一次优化,js 在页面呈现效果上的作用越来越弱,一些场景下我们不需要 js ,仅仅通过 css 就能够实现绑定元素并基于该元素对页面的呈现效果进行改变。
总结
可通过 class 来表示状态,在纯展示类交互设计中应该追求无 js 方案,避免不必要的由 js 直接操作样式的代码。
组件封装
我们可以将一个完整的网页分为一个个组件,每一个组件是包含 HTML、JS、CSS 的单元,良好的组件具有封装性、正确性、拓展性、复用性
轮播图
- HTML
<div id="my-slider" class="slider-list">
<ul>
<li class="slider-list_item-selected">
<img width="500px" src="./picture/1.jpg" />
</li>
<li class="slider-list_item">
<img width="500px" src="./picture/2.jpeg" />
</li>
</ul>
</div>
在 HTML
中我们声明了网页的结构,在轮播图中我们通过 ul、li、img
来布局放置我们的图片
- CSS
#my-slider {
position: relative;
width: 500px;
}
.slider-list ul {
list-style-type: none;
position: relative;
padding: 0;
margin: 0;
}
.slider-list_item,
.slider-list_item-selected {
position: absolute;
transition: 1s;
opacity: 0;
text-align: center;
}
.slider-list_item-selected {
transition: 1s;
opacity: 1;
}
在 CSS 中我们通过 opacity 来控制图片的显示和隐藏,用 transition 来控制过程动画时长,用 position 来实现图片的定位重叠
- JS
class Slider {
constructor(id) {
this.container = document.getElementById(id);
this.items = this.container.querySelectorAll('.slider-list_item, .slider-list_item-selected')
}
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';
}
}
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);
}
}
const slider = new Slider('my-slider');
setInterval(() => {
slider.slideNext();
}, 2000)
在 JS 中我们主要定义函数来获取当前所在图片的 index,根据当前的 index 和图片总数实现图片的轮回交换
总结
在实现这个简单的轮播图的过程中,遵循了以下的方法:
- 结构设计:我们先考虑如何将需要的元素按照需求能够正确地在界面上排布出来,在这个过程中每个元素对应的标签应该选用什么,用什么标签能够更好地实现需求的功能
- 展现效果:主要涉及到两个方面,一方面是元素的 UI 设计,美观且交互良好,另一方面是定位,怎么合理地运用定位属性来减少我们的代码量,在实现布局的同时优化屏幕自适应
- 行动设计:API(功能) 主要是定义函数来实现所需要的操作,EVENT(控制流) 主要是自定义事件来解耦
重构:插件化
解耦:将控制元素抽取成插件,插件和组件之间通过依赖注入来建立联系
class Slider {
constructor(id, cycle = 2000) {
this.container = document.getElementById(id);
this.items = this.container.querySelectorAll('.slider-list_item, .slider-list_item-selected')
this.cycle = cycle;
const previous = this.container.querySelector('.slide-list_previous');
if (previous) {
previous.addEventListener('click', evt => {
this.stop();
this.slidePrevious();
this.start();
evt.preventDefault();
});
}
}
}
const slider = new Slider('my-slider');
slider.start();
如上述代码我们可以通过在外部封装一个 function pluginPrevious()
来实现
class Slider {
constructor(id, cycle = 2000) {
this.container = document.getElementById(id);
this.items = this.container.querySelectorAll('.slider-list_item, .slider-list_item-selected')
this.cycle = cycle;
}
}
function pluginPrevious(slider) {
const previous = slider.container.querySelector('.slide-list_previous');
if (previous) {
previous.addEventListener('click', evt => {
slider.stop();
slider.slidePrevious();
slider.start();
evt.preventDefault();
});
}
}
const slider = new Slider('my-slider');
slider.registerPlugins(pluginPrevious)
slider.start();
这样在我们需要对轮播图增加新功能或者修改老功能的时候我们都不再需要修改 Slider 类中的东西,只需要修改外部的 function,修改传入 registerPlugins 的参数即可,很好地提高了代码的可拓展性、维护性等等