公司内部管理系统准备改版,最近几天把前端架子搭起来了。在规划底层元素时参考了BootstrapAmaze UI,一些组件的实现确实很优雅。
比如Amaze UI 的单/复选框。

之前我在模拟单/复选框时,都是很暴力的用 js 解决。

  1. 绑定点击事件;
  2. 更改 checked 属性值;
  3. 切换 class 变更样式。

而打开控制台观察Amaze UI的单/复选框,发现Elements状态是无更改的。
更改checked属性值,并不会影响到标签字面量属性。但是想要切换选中和未选中的样式,如果不是通过切换class来实现,那便是直接修改DOM了,而这两种操作在控制台中的都能看到一个状态变化的响应。

那么Amaze究竟是怎么实现的呢?

我选中一个元素的图标,在Styles中观察样式变化。直到发现了伪类选择器:checked和相邻选择器+,才恍然大悟。

实例

<label for="sex1" class="form-check">
<input type="radio" id="sex1" checked name="sex" value="1">
<span class="check-icons">
<i class="icon icon-check">&#xe632;</i>
<i class="icon icon-uncheck">&#xe634;</i>
</span>
</label>
<label for="sex2" class="form-check" >
<input type="radio" id="sex2" name="sex" value="0">
<span class="check-icons">
<i class="icon icon-check">&#xe632;</i>
<i class="icon icon-uncheck">&#xe634;</i>
</span>
</label>

图标使用的iconfont,然后添加一些样式,我使用less作为css预处理语言。

.form-check {
display: inline-block;
position: relative;
margin-right: 1em;
padding-left: 24px;
line-height: 28px;
cursor: pointer;
input {
display: none;
&:checked+.check-icons {
.icon-check {
display: inline-block;
}
.icon-uncheck {
display: none;
}
}
}
.check-icons {
position: absolute;
top: 5px;
left: 0;
width: 18px;
height: 18px;
text-align: center;
.icon {
position: absolute;
top: 0;
left: 0;
line-height: 18px;
font-size: 18px;
}
.icon-uncheck {
color: #d7d7d7
}
.icon-check {
color: #4776c8;
display: none;
}
}
}
  • 通过label:for属性来触发input:checked属性值更改。
  • 设置相邻选择器,由:checked状态变化影响对应的icon显示和隐藏。

兼容性

相邻选择器+兼容IE7+,而伪类选择器:checkedlabel:for属性兼容IE9+(又是这一道坎…)。

IE7、8的兼容

由选择器的妙用,让我想到了属性选择器[checked],兼容性为IE7+。

.ie8 [checked]+.check-icons .icon-check,
.ie7 [checked]+.check-icons .icon-check {
display: inline-block;
}
.ie8 [checked]+.check-icons .icon-uncheck,
.ie7 [checked]+.check-icons .icon-uncheck {
display: none;
}

我针对IE7、8应用了这些样式,for属性失去了作用,就需要绑定点击事件来更改checked值。
做完之后发现并没有什么卵用。

因为属性选择器是针对标签字面量属性启作用,而如input.checked = true;这种修改方式是不会影响到标签上定义的属性。

这就牵扯到了attributeproperty的区别。

var ck = document.createElement('input');
ck.type = 'checkbox';
document.body.appendChild(ck);
ck.checked = true;
ck.getAttribute('checked'); // null
ck.checked = false;
ck.setAttribute('checked','checked');
ck.checked // false
  • attribute 是 html 标签的属性
  • property 是 Dom 标准属性

也许是因为标签自定义属性的存在,这两个属性值是不同步的。

如果想要使[checked]选择器生效,势必要在点击事件中同步修改attributeproperty

实验后发现在IE7、8下正确运行了,对应的icon的display属性确实变化了,但是肉眼看不到样式更改。
不知道是不是渲染能力差的原因,总是需要在控制台里点击对应的标签后才看得到更改后的样式。

思前思后,既然总归是需要用 js 去兼容,那在IE7、8下还是使用切换class来控制样式。