之前我写了一篇自定义 checkbox 的文章,通过 css3 实现自定义的 checkbox,并没有使用当今流行的 Reactjs, 或者 Vuejs 之类的进行组件化。但是很显然,这样封装的 checkbox 组件复用的时候非常麻烦。如果在新项目中使用的话,可能需要同时拷贝 css 和 html 文件进行整合。从 html 语义角度上讲,代码的易读性也不是很强,显然这样的组件显然不利于维护。
其实 Web Component 是前端界一直非常热衷的一个领域,因为原生的 HTML 在维护复杂网页应用时,实在是太差了。所以才出现了诸如 Google 的Ploymer
、Facebook 的Reactjs
等等。而且很多MVVM
的框架也自带组件化的方案,例如Angularjs
的指令,但貌似ng
的这个用起来太复杂。用第三方组件化的框架去实现的话,你需要依赖框架本身很多东西,很多时候我们只是简单的几个组件,不是很大,也不是很多,所以为了保证组件的轻量,简单
,其实这个时候我们并不想采用第三方的框架。接下来我会介绍使用Shadow DOM和registerElement
的方式去实现组件化。
先看看实现后的调用方式:
<div class="line">
<label>checkbox1
</label>
<check-box class="mycheck" checked="true" id="ComCheckbox"></check-box>
</div>
<div class="line">
<label>checkbox2
</label>
<check-box class="mycheck" checked="false" id="ComCheckbox1" value="2"></check-box>
</div>
看起来是不是很简洁,调用自定义的checkbox
组件不需要那么多扰乱阅读的元素,只需要一个明确的check-box
标签,既可以表示checkbox
组件。效果如下:
好了看了效果,我们来看看具体怎么实现的吧。在线 demo 查看
组件的组成
通常情况下,我们一个组件一般是由html
模板,css
样式,js
脚本逻辑三部分组成的。他们的作用我就不多废话了。至于当前组件的 css 样式自定义方法请看我上一篇文章CSS3 实现自定义 checkbox,这里我就不重复这部分了。
- 在项目工作区新建一个
component-checkbox.html
文件, 这个文件会被当做整个组件,在我们需要引用的页面中通过link
标记动态的引入。component-checkbox.html
文件即包含了 HTML 模板,CSS 样式,JS 三个部分,他们在组件文件中的分布如下:
<template>
<style>// 放CSS样式定义</style>
// 放HTML标记
</template>
<script type="text/javascript">
// JS脚本逻辑
</script>
具体 HTML/CSS 定义
<template id="CheckBox">
<style>
.slide-checkbox {
position: relative;
width: 120px;
height: 40px;
line-height: 40px;
border-radius: 30px;
background: #4fbe79;
}
.slide-checkbox input[type=checkbox] {
visibility: hidden;
}
.slide-checkbox label {
position: absolute;
height: 30px;
width: 30px;
left: 5px;
top: 5px;
background: #FFFFFF;
border-radius: 50% 50%;
-webkit-transition: all .4s ease;
-moz-transition: all .4s ease;
-o-transition: all .4s ease;
-ms-transition: all .4s ease;
transition: all .4s ease;
}
.slide-checkbox input[type=checkbox]:checked + label {
left: 85px;
}
</style>
<div class="slide-checkbox">
<input type="checkbox" name="checkbox" id="SlideCheck" />
<label for="SlideCheck"></label>
</div>
</template>
JS 的实现
这种组件实现发方法,重点地方就在 JS 脚本这个部分,所以请看下面的详细描述。
1. Shadow DOM 说明
Shadow DOM 提供了一种独立封装`html’, ‘css’, ‘js’到组件文件的一种方法,这样 Shadow DOM 内部的样式文件及 js 等等都与引用页面处于隔离状态,互相独立,所以不必担心他们之间会不会出现样式,js 相互乱引用的情况出现。当然调用页面与 Shadow DOM 的通信则需要通过 js 来完成。
2. registerElement 说明
可以在浏览器中实现自定义 element, 当然会有人想到说’document.createElement()‘方法也可以创建不同的元素,但是很显然registerElement
更强大些,具体就不展开了。
3. 详细 JS 代码
// Whether registerElement is supported
function isCustomElementSupported() {
return 'registerElement' in document;
}
(function() {
"use strict";
if (isCustomElementSupported()) {
var objectPrototype = Object.create(HTMLElement.prototype);
var selfDoc = document.currentScript.ownerDocument;
Object.defineProperty(objectPrototype, 'value', {
get: function() {
return this.getAttribute("value") || null;
},
set: function(value) {
this.setAttribute("value", value);
}
});
Object.defineProperty(objectPrototype, 'checked', {
get: function() {
return this.getAttribute("checked") || false;
},
set: function(isChecked) {
shadowChecked(this, isChecked);
this.setAttribute("checked", isChecked);
}
});
objectPrototype.createdCallback = function() {
var self = this;
var rootElement = self.createShadowRoot();
var templateContent = selfDoc.querySelector("#CheckBox").content;
var nodes = document.importNode(templateContent, true);
// Add template content to shadowRoot element
rootElement.appendChild(nodes);
var checkbox = rootElement.querySelector("#SlideCheck");
// init checked value
if (self.checked == "true") {
checkbox.checked = true;
}
// Add change event to checkbox
checkbox.addEventListener('change', function() {
self.checked = this.checked;
});
};
var checkbox = document.registerElement('check-box', {
prototype: objectPrototype
});
}
// update shadow root
function shadowChecked(self, isChecked) {
var shadowCheck = self.shadowRoot.querySelector("#SlideCheck");
shadowCheck.checked = isChecked;
}
})();
4. 代码描述
该代码片段先定义了一个registerElement
支持情况的检测方法。在匿名函数中先经过支持检测后,通过Object
对象创建了有一个HTMLElement
的原型对象,用于注册 checkbox 继承HTMLElement
时使用。 同时我们在原型对象上定义了checked
和value
对象,方便调用组件时使用。通过get
,set
方法我们与其相对应的checked
和value
属性(attribute)产生联系,便于数据通信。
createdCallback
方法在组件被创建后执行。首先将获取到template
里的内容添加到rootElement
中, 添加完后我们便可以进行一些初始化组件的操作。例如初始化checked
的状态值, 模板内置的checkbox
添加change
的事件绑定,便于实时更新组件当前的checked
值。
组件的使用
1. 引用组件
首先我们需要在要使用该组件的 HTMl 页面中引入组件文件。 注:由于该文件是用过 HTTP 读取过来的,所以静态项目可能无法获取组件文件,需要把这些文件放到服务器目录下去访问。 引入方式:
<link rel="import" href="com-checkbox.html">
2. 使用
在需要使用该组件的位置放入组件标签即可:
<check-box class="mycheck" checked="true" value="1" id="ComCheckbox"></check-box>
注:class
为样式类名,checked 为初始化状态, value 为 checkbox 值。
当然你也可以多次引用组件,例如同时使用 4 个,实际状况中可能会更多:
<div class="line">
<label>checkbox1
</label>
<check-box class="mycheck" checked="true" value="1" id="ComCheckbox"></check-box>
</div>
<div class="line">
<label>checkbox2
</label>
<check-box class="mycheck" checked="false" value="2" id="ComCheckbox1" value="2"></check-box>
</div>
<div class="line">
<label>checkbox3
</label>
<check-box class="mycheck" value="3" checked=false id="ComCheckbox2" value="3"></check-box>
</div>
<div class="line">
<label>checkbox4
</label>
<check-box class="mycheck" value="4" checked="true" id="ComCheckbox3" value="4"></check-box>
</div>
3. 获取 check-box 的状态和值
var checkbox = document.querySelector("#ComCheckbox");
console.log(checkbox.checked); // output: true
console.log(checkbox.value); // output: 1
根据 ID 获取元素,可通过checked
和value
对象直接访问。
4. 实现 checkbox 全选和取消
var checkboxs = document.querySelectorAll("com-checkbox");
function selectAll() {
for (i = 0; i < checkboxs.length; i++) {
checkboxs[i].checked = true;
}
}
function cancleSelectAll() {
for (i = 0; i < checkboxs.length; i++) {
checkboxs[i].checked = false;
}
}
获取所以的checkbox
列表, 然后遍历改变checked
的状态。其他复杂操作或者情况可以再组件定义时进行扩展。
总结
以上便是整个组件定义的具体方法。当然今天我们自定义的是checkbox
组件,我们当然可以通过该方法去实现其他的组件,例如radio
,select
等等。
原文地址:http://imziv.com/blog/article/read.htm?id=72
最后修改于 2016-04-17