<div id="app">
<h2 class="title" data-on="title"></h2>
<div class="content" data-on="content"></div>
<div class="count" data-on="count"></div>
<div class="control">
<label for="content-input">请输入内容:</label>
<input type="text" class="content-input" placeholder="请输入内容" id="content-input" /></div>
<button class="add" onclick="">加1</button>
</div>
<div class="skip">
<a href="https://codepen.io/collection/DRKGbz/" target="_blank">Vue案例全集</a>
</div>
@import url(https://fonts.googleapis.com/css?family=Montserrat);
$colour:hsla(350,100%,25%,1);
$grey:desaturate($colour,90%);
body {
background:lighten($grey,30%);
background-image:
linear-gradient(40deg,transparentize($grey,0.95),transparentize($colour,0.95)),
linear-gradient(135deg,
lighten($grey,18%) 0%,
lighten($grey,18%) 10%,
lighten($grey,25%) 11%,
lighten($grey,25%) 40%,
lighten($grey,35%) 41%,
lighten($grey,35%) 50%,
lighten($grey,18%) 51%,
lighten($grey,18%) 60%,
lighten($grey,25%) 61%,
lighten($grey,25%) 90%,
lighten($grey,35%) 91%)
;
background-size:7px 7px, 4px 4px;
display: flex;
justify-content: center;
align-items: center;
font-family: montserrat;
}
html,body {
width: 100vw;
height: 100vh;
}
#app {
background: white;
border: 0 none;
border-radius: 3px;
box-shadow: 0 0 15px 1px rgba(0, 0, 0, 0.4);
padding: 20px 30px;
box-sizing: border-box;
text-align: center;
}
.title {
margin: 0 0 25px;
display: inline-block;
border-bottom: 2px solid #69f;
padding-bottom: 5px;
color: #69f;
text-shadow: 1px 1px 0 rgba(#000, .4);
}
.control {
margin-top: 20px;
margin-bottom: 20px;
label{
display: inline-block;
vertical-align: middle;
}
input {
height: 40px;
padding: 5px 10px;
box-sizing: border-box;
border-radius: 3px;
border: 1px solid rgba(#000, .5);
box-shadow: 0 0 3px rgba(#000, .15);
&:focus,
&:active {
outline: none 0;
box-shadow: 0 0 4px rgba(0, 169, 244, .9);
border-color: rgba(0, 169, 244, 1);
}
}
}
button {
color: #454545;
background: transparent;
border: 2px solid #454545;
position: relative;
margin: 1em auto;
padding: 0.5em 1em;
transition: all 0.3s ease-in-out;
text-align: center;
font-family: comfortaa;
font-weight: bold;
font-size: 2rem;
cursor: pointer;
}
button:before, button:after {
content: '';
display: block;
position: absolute;
border-color: #454545;
box-sizing: border-box;
border-style: solid;
width: 1em;
height: 1em;
transition: all 0.3s ease-in-out;
}
button:before {
top: -6px;
left: -6px;
border-width: 2px 0 0 2px;
z-index: 5;
}
button:after {
bottom: -6px;
right: -6px;
border-width: 0 2px 2px 0;
}
button:hover:before, button:hover:after {
width: calc(100% + 12px);
height: calc(100% + 12px);
border-color: #fff;
}
button:hover {
color: #353535;
background-color: #fff;
}
button:active, button:focus {
outline: none;
}
.content {
margin: 15px auto 20px;
}
.count {
font-size: 2rem;
display: inline-block;
vertical-align: middle;
}
.skip{
position: fixed;
right: 0;
bottom: 0;
padding: 10px 30px;
background: rgba(0,0,0,.5);
a {
color: #fff;
text-decoration: none;
text-shadow: 1px 1px 0 rgba(0, 0, 0, .15);
}
}
(function() {
// 数据
window.data = {
title: '数据视图单向绑定',
content: '使用属性描述器实现数据视图绑定',
count: 0
};
var attr = 'data-on'; // 约定好的语法,声明DOM绑定对象属性
// 使用发布/订阅模式,集中管理监控和触发回调事件
var Observer = {
watchers: {},
subscribe: function(key) {
var el = document.querySelector('[' + attr + '="'+ key + '"]');
// demo
var cb = function react(val) {
el.innerHTML = val;
}
if (this.watchers[key]) {
this.watchers[key].push(cb);
} else {
this.watchers[key] = [].concat(cb);
}
},
emit: function(key, val) {
var len = this.watchers[key] && this.watchers[key].length;
if (len && len > 0) {
for(var i = 0; i < len; i++) {
this.watchers[key][i](val);
}
}
}
};
// 为对象中每一个属性设置描述对象,尤其是存取器函数
function defineDescriptors(obj) {
for (var key in obj) {
// 遍历属性
defineDescriptor(obj, key, obj[key]);
}
// 为特定属性设置描述对象
function defineDescriptor(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function() {
var value = val;
return value;
},
set: function(newVal) {
if (newVal !== val) {
// 值发生变更才执行
val = newVal;
Observer.emit(key, newVal); // 触发更新DOM
}
}
});
Observer.subscribe(key); // 为该属性注册回调
}
}
// 初始化demo
function init() {
defineDescriptors(data); // 处理数据对象
var eles = document.querySelectorAll('[' + attr + ']');
// 初始遍历DOM展示数据
// 其实可以将该操作放到属性描述对象的get方法内,则在初始化时只需要对属性遍历访问即可
for (var i = 0, len = eles.length; i < len; i++) {
eles[i].innerHTML = data[eles[i].getAttribute(attr)];
}
// 辅助测试实例
document.querySelector('.add').addEventListener('click', function(e) {
data.count += 1;
});
document.querySelector('.content-input').addEventListener('input', function(e) {
data.content = e.target.value;
});
}
init();
})();