视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
如何自动获取HTML5的data-*属性示例代码详解
2020-11-27 15:11:45 责编:小采
文档


项目中需要设计一系列的自定义标签,于是涉及到标签的属性如何访问。使用 HTML5 的 data-* 属性的话,获取元素后直接访问 dataset.属性名称 即可,dataset 的类型为 DOMStringMap {},一个 MAP 对象,仍是 key / value 式的对象,使用上比较方便。但却遇到一个兼容性的问题, 在 Android 2.3 等的一些旧有浏览器上尚不支持。

对此,我们可以写一个兼容性的补丁。主要原理是在“劫持” document.querySelector/querySelectorAll 这类获取元素的方法之后,通过提供自定义的字段 dataset = {....} 即可实现类似标准写法。当然,简单地,你可以通过一个 API 方法来提供类似 getDataAttrib() 获取属性,效果一样。我们之所以不采取这种方式,而采用另外一种方式,是为了更好向标准靠拢,使用大家都一致的访问方式。

我的实现如下:

// 如浏览器不支持 HTML5 data-* 属性,设置一个。
;(function(){
	// 测试元素
	var el;
	el = document.createElement('p');
	el.setAttribute('data-id', '111');
	if(!el.dataset){
	Element.prototype.dataset = {};
	
	var querySelectorAll = document.querySelectorAll; // 保存一个
	document.querySelectorAll = function(){
	var resultEls = querySelectorAll.apply(this, arguments);
	for(var resultEl, i = 0, j = resultEls.length; i < j; i++){
	resultEl = resultEls[i];
	resultEl.dataset = getAttrib(resultEl.attributes)
	}
	
	return resultEls;
	}
	
	// 也就是单个的 document.querySelectorAll()。不保存,直接覆盖
	document.querySelector = function(){
	var resultEls = document.querySelectorAll.apply(this, arguments);
	return resultEls ? resultEls[0] : null;
	};	
	
	}
	el = null; // 要完全移除 dummy 元素,是否这样就 ok?
	
	/**
	 * 把元素保存为 JSON 对象
	 * @param {Element.attributes} 元素属性集合
	 * @return {Object}
	 */
 function getAttrib(attributes) {
 if (!attributes) return;
 var hash = {};
 
 for (var attribute, i = 0, j = attributes.length; i < j; i++) {
 attribute = attributes[i];
 if(attribute.nodeName.indexOf('data-') != -1){
	 hash[attribute.nodeName.slice(5)] = attribute.nodeValue;
 }
 }
 
 return hash;
 }
})();

考虑到 querySelector 获取元素的方式已经足够,故所以当前没有提供 documeny.getElementByID(“#id”) 方法。

请注意:对于非 document 对象身上执行的 querySelector /querySelectorAll 将不支持,仍不会返回 dataset。 针对该问题,已于2013.1.16 通过重写 Element.prototype 方法解决。详细过程如下:

if(!canSupportDataSet()){
 Element.prototype.dataset = {};


 modifyQuerySelectorAll_By(document); // document 的好像不一样……
 modifyQuerySelectorAll_By(Element.prototype);
}


/**
 * 覆盖系统的 querySelector/querySelectorAll 方法。
 * @param host {Element.prototype/Document}
 */
function modifyQuerySelectorAll_By(host){
 var querySelectorAll = host.querySelectorAll; // 保存一个
 host.querySelectorAll = function(){
 var resultEls = querySelectorAll.apply(this, arguments);
 for(var resultEl, i = 0, j = resultEls.length; i < j; i++){
 resultEl = resultEls[i];
 resultEl.dataset = getAttrib(resultEl.attributes)
 }


 return resultEls;
 }


 // 也就是单个的 document.querySelectorAll()。不保存,直接覆盖
 host.querySelector = function(){
 var resultEls = host.querySelectorAll.apply(this, arguments);
 return resultEls ? resultEls[0] : null;
 };
}

测试例子:

<listview id="foo" data-id="1">
 Hello World
 <p data-id="2"></p>
</listview>

<script>
var el = document.querySelector('#foo');
alert(el.querySelector('p').dataset.id);
</script>

问题小结:

  1. 浏览器必须能够支持 querySelector/querySelectorAll 方法,否则该方法也没有意义;

  2. 只能从获取元素的方法提供 dataset。比如事件处理器中参数 e 的 e.tartget. dataset 则是空对象。

  3. 如上代码所示,单个的 querySelector() 是经过 querySelectorAll(),当中有遍历数组的操作,能否适当优化一下,使用原生的来做?

  4. 对于 CSS Selector Engine 速度敏感的同学,本方法不适用。因为修改系统方法,可见必然性能会下降。但可以保证,这种下降是属于小幅度的;

  5. 尚不支持 documeny.getElementByID,待增加之。

下载本文
显示全文
专题