共计 2867 个字符,预计需要花费 8 分钟才能阅读完成。
在开发中经常会在 DOM 上存储一些自定义数据,我们可以通过 setAttribute 方法来实现。但是当数据为引用类型时,存储后的数据却无效。这里将用原生的 JS 对 data 方法进行实现。
使用 setAttribute:
<div id="test-data"></div>
<p class="test-data-list"></p>
<p class="test-data-list"></p>
<p class="test-data-list"></p>
<p class="test-data-list"></p>
var testData = document.querySeletor('#test-data');
testData.setAttribute('baukh', {a:1,b:2})// 执行后 DOM 节点变化为 <div baukh="[object Object]"></div>
testData.getAttribute('baukh'); // => "[object Object]"
可以从上面的代码中看出,存进去的是个 Object,取出来的是 Object.toString()所产出的字符串。
分析
在 JS 经典类库 -jQuery 中存在 data 方法是通过 jQuery.cache 的方式进行数据存储,那么还有没有其它方法可以实现?
由于使用场景不同,我想实现的方式是将数据直接存储到 DOM 节点上,以达到使用时更方便简捷的目的。
那如何存储? 变量 testData 存储的是通过 document.querySeletor(‘#test-data’)获取到的 Element,而 Element 是 Object 的一个实例。通过 [testData instanceof Object] 可以进行验证。
那么一切都简易了,即然是 Object 类型,那么就可以随意的增删自定义属性。
通过在 Element 的原型上增加 data 方法来实现 DOM 扩展
Element.prototype.data = function(key, value){
var _this = this,
_dataName = 'testData', // 存储至 DOM 上的对象标记, 这里只是测试用名
_data = {};
// 未指定参数, 返回全部
if(typeof key === 'undefined' && typeof value === 'undefined'){return _this[_dataName];
}
// setter
if(typeof(value) !== 'undefined'){
// 存储值类型为字符或数字时, 使用 attr 执行
var _type = typeof(value);
if(_type === 'string' || _type === 'number'){_this.setAttribute(key, value);
}
_data = _this[_dataName] || {};
_data[key] = value;
_this[_dataName] = _data;
return this;
}
// getter
else{_data = _this[_dataName] || {};
return _data[key] || _this.getAttribute(key);
}
};
这里来试一下:
var testData = document.querySelector('#test-data');
// 字符串类型测试
testData.data('name', 'baukh');
console.log(testData.data('name')); // => 'baukh'
// 对象类型测试
testData.data('info', {'name': 'baukh', 'age': 27});
console.log(testData.data('info')); // => Object {name: "baukh", age: 27}
解决 NodeList 存储
现在还有一个问题, 通过 Element.prototype 绑定的方法只支持 Element 类生效, 而对 NodeList 类并无效果.
可以通过下面这些代码进行效果测试:
var testDataList = document.querySelectorAll('.test-data-list'); // 获取的为 NodeList 而非 Element
testDataList.data('name', 'baukh'); // Uncaught TypeError: testDataList.data is not a function
这肯定不是想要的结果, 那么 NodeList 类就需要如下处理:
NodeList.prototype.data = function (key, value) {
// setter
if(typeof(value) !== 'undefined'){[].forEach.call(this, function (element, index) {element.data(key, value);
});
return this;
}
// getter
else{return this[0].data(key, value); // getter 将返回第一个
}
};
来测试下 NodeList 类的 data 实现:
var testDataList = document.querySelectorAll('.test-data-list'); // 获取的为 NodeList 而非 Element
testDataList.data('name', 'baukh'); // Uncaught TypeError: testDataList.data is not a function
// 字符串类型测试
testDataList.data('name', 'baukh');
console.log(testDataList.data('name')); // => 'baukh'
// 对象类型测试
testDataList.data('info', {'name': 'baukh', 'age': 27});
console.log(testDataList.data('info')); // => Object {name: "baukh", age: 27}
这样就功能上就完成了.
当然也可以将 NodeList 与 Element 进行互换, 具体情况具体考虑.
很简单不是吗?
顺带说一下,Array 类型的数据,也可以增加自定义属性。
var ar = [1,2,3];
console.log(ar instanceof Object); //true 能添加自定义属性的原因就在这里,Array 也是 Object 的实例。ar.test1 = {a:1,b:2};
console.log(ar); //[1, 2, 3, test1: Object]
console.log(ar.test1); //Object {a: 1, b: 2}
随笔一行
这是前端最好的时代,这也是前端最坏的时代。众多前端框架满天飞,随着 jQuery 在前端行业的慢慢弱化,总是会有一种斯人远去,何者慰籍的感觉。互勉吧,各位。
另推荐个表格组件 gridManager
正文完
发表至: javascript
2019-05-27