构造一个 DOMTokenList/DOMSettableTokenList 实例
Constructing a DOMTokenList/DOMSettableTokenList instance
DOMTokenList和DOMSettableTokenList接口(MDN,WHATWG(提供了操作由空格分隔字符串表示的有序字符串标记集的方法。它们最常以 Element.prototype.classList 属性的形式使用,该属性是一个 DOMTokenList,它反映了关联元素的 class
属性。
var div = document.createElement('div');
div.setAttribute('class', 'hello world goodnight moon');
var list = div.classList;
console.assert(list.length === 4);
console.assert(list[0] === 'hello');
console.assert(list.item(1) === 'world');
console.assert(list.contains('moon') === true);
console.assert(list.contains('mars') === false);
list.remove('world', 'earth', 'dirt', 'sand');
list.add('hello', 'mars');
list.toggle('goodnight');
console.assert(div.getAttribute('class') === 'hello moon mars');
我正在研究一个自定义元素(HTML5Rocks,W3C Draft(,它显示了指定Stack Overflow用户活动的实时提要。此用户列表在 ids
属性中指定,并且可能随时更新。
<so-users ids="1114 22656 106224"></so-users>
document.querySelector('so-users').setAttribute('ids', '23354 115866');
我不想要求用户直接操作此属性,而是希望有一个 .ids
属性提供他们可以改用的 DOMTokenList。理想情况下,这将直接与属性相关联,但是我必须手动绑定的未绑定的 DOMSettableTokenList 实例也可以。
document.querySelector('so-users').ids.add('17174');
不幸的是,我一直找不到任何方法来创建 DOMTokenList 实例。该定义不是构造函数,当我调用任何关联的方法时,直接使用其原型创建对象会导致错误:
new DOMTokenList; // TypeError: Illegal constructor
new DOMSettableTokenList; // TypeError: Illegal constructor
var list = Object.create(DOMSettableTokenList.prototype, {
value: { value: 'hello world' }
});
console.assert(list instanceof DOMTokenList);
console.assert(list instanceof DOMSettableTokenList);
list.item(0); // TypeError: Illegal invocation
function TokenListConstructor() {
this.value = 'hello world';
}
TokenListConstructor.prototype = DOMSettableTokenList.prototype;
var list = new TokenListConstructor;
console.assert(list instanceof DOMTokenList);
console.assert(list instanceof DOMSettableTokenList);
list.add('moon'); // TypeError: Illegal invocation
如何构建新的DOMTokenList
或DOMSettableTokenList
实例?
不能直接创建 DOMTokenList 或 DOMSettableTokenList。相反,您应该使用 class 属性来存储和检索数据,并可能将 DOM 元素的 ids 属性映射到 classList 属性。
var element = document.querySelector('so-users');
element.ids = element.classList;
您可以根据文档使用 relList,但 classList 更受支持,唯一的缺点是,如果您的一个 id 与类名匹配,您可能会遇到问题,因此请设置内联样式以隐藏元素以防万一。
对于自定义组件,兼容性应该是一个问题(classList 存在于 IE>=10、Firefox 3.6、Chrome 8、Opera 11.5 和 Safari 5.1 中,请参阅 http://caniuse.com/#feat=classlist(,因此,如果兼容性在您的要求中,请使用下面发布的另一个解决方案。
如果您不能使用 clases 或 classList 和/或必须使用 ids 属性,则应根据规范实现具有以下属性的自定义函数作为函数。
- 项((
- 包含((
- 添加((
- 删除((
- 切换((
这是此类功能的示例实现。
var TokenList = function (ids) {
'use strict';
var idsArray = [],
self = this,
parse = function (id, functionName, cb) {
var search = id.toString();
if (search.split(' ').length > 1) {
throw new Error("Failed to execute '" + functionName + "' on 'TokenList': The token provided ('" + search + "') contains HTML space characters, which are not valid in tokens.');");
} else {
cb(search);
}
};
function triggerAttributeChange() {
if (self.tokenChanged && typeof self.tokenChanged === 'function') {
self.tokenChanged(idsArray.toString());
}
}
if (ids && typeof ids === 'string') {
idsArray = ids.split(' ');
}
self.item = function (index) {
return idsArray[index];
};
self.contains = function (id) {
parse(id, 'contains', function (search) {
return idsArray.indexOf(search) !== -1;
});
};
self.add = function (id) {
parse(id, 'add', function (search) {
if (idsArray.indexOf(search) === -1) {
idsArray.push(search);
}
triggerAttributeChange();
});
};
self.remove = function (id) {
parse(id, 'remove', function (search) {
idsArray = idsArray.filter(function (item) {
return item !== id;
});
triggerAttributeChange();
});
};
self.toggle = function (id) {
parse(id, 'toggle', function (search) {
if (!self.contains(search)) {
self.add(search);
} else {
self.remove(search);
}
});
};
self.tokenChanged = null;
self.toString = function () {
var tokens = '',
i;
if (idsArray.length > 0) {
for (i = 0; i < idsArray.length; i = i + 1) {
tokens = tokens + idsArray[i] + ' ';
}
tokens = tokens.slice(0, tokens.length - 1);
}
return tokens;
};
};
使用此函数的新实例在元素中设置"ids"属性,最后必须将目标属性绑定到侦听元素更改并更新属性的属性,反之亦然。您可以使用突变观察者来做到这一点。
请参阅 DOM 属性更改和 https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver 上的触发事件
var attachTokenList = function (element, prop, initialValues) {
'use strict';
var initValues = initialValues || element.getAttribute(prop),
MutationObserver = window.MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
observer,
config,
cancelMutation = false;
function createTokenList(values) {
var tList = new TokenList(values);
tList.tokenChanged = function () {
element.setAttribute(prop, element[prop].toString());
cancelMutation = true;
};
element[prop] = tList;
}
createTokenList(initValues);
observer = new MutationObserver(function (mutation) {
var i,
mutationrec,
newAttr;
if (mutation.length > 0 && !cancelMutation) {
for (i = 0; i < mutation.length; i = i + 1) {
mutationrec = mutation[i];
if (mutationrec.attributeName === prop && element[prop]) {
newAttr = element.getAttribute(prop);
createTokenList(newAttr);
}
}
}
cancelMutation = false;
});
config = {
attributes: true
};
observer.observe(element, config);
};
测试以查看它是否有效
<so-users ids="1234 5678"></so-users>
<button onclick="clickButton1()">Add 7890</button>
<button onclick="clickButton2()">Set to 3456</button>
<button onclick="clickButton3()">Add 9876</button>
在脚本标记内
var elem = document.querySelector('so-users');
attachTokenList(elem, 'ids')
function clickButton1 () {
elem.ids.add('7890');
}
function clickButton2 () {
elem.setAttribute('ids', '3456');
}
function clickButton3 () {
elem.ids.add('9876');
}
按顺序单击按钮将 ids 属性设置为"3456 9876">
您可以使用此函数获取 DOMTokenList 的实例:
function newDOMTokenList(initialTokens) {
const tmp = document.createElement(`div`);
const classList = tmp.classList;
if (initialTokens) {
initialTokens.forEach(token => {
classList.add(token);
});
}
return classList;
}
我们可以从div 中"窃取"DOMTokenList,因为它不会影响当前文档,直到您选择插入元素(例如通过使用 insertAdjacentElement(,并且它将被垃圾回收,因为我们不保留对变量的任何引用tmp
。
然后,您可以使用您的列表:
var list = newDOMTokenList(['a', 'b']);
list.add('c');
list.contains('d'); // false
list.contains('b'); // true
list.item(1) // 'b'
list instanceof DOMTokenList // true
// etc...
// render it to a string
var soUsers = document.querySelector('so-users');
soUsers.setAttribute('ids', list.toString());
您甚至可以将 MutationObserver 添加到 tmp
元素中,并在classList
更改时获取回调:
function newDOMTokenList(initialTokens, changed) {
const tmp = document.createElement('div');
const classList = tmp.classList;
if (initialTokens) {
initialTokens.forEach(token => {
classList.add(token);
});
}
if (changed) {
const observer = new MutationObserver((mutationList, observer) => {
for (const mutation of mutationList) {
if (mutation.attributeName === 'class') {
changed();
}
}
});
observer.observe(tmp, {attributes: true});
}
return classList;
}
但是,这将导致tmp
div永远不会被垃圾回收,因为MutationObserver 需要保留对它的引用。
利用自定义元素 - 添加JS属性和方法初始化方法,HTMLElement.dataset 尝试
var XFooProto = Object.create(HTMLElement.prototype);
// 1. Give x-foo a foo() method.
XFooProto.contains = function(id) {
var data = JSON.parse(this.dataset.ids);
return data.some(function(_id) {
return id == _id
})
};
XFooProto.add = function(id) {
var data = JSON.parse(this.dataset.ids);
if (!this.contains(id)) {
data.push(id);
};
return data
};
XFooProto.remove = function(id) {
var data = JSON.parse(this.dataset.ids);
if (this.contains(id)) {
for (var _id in data) {
if (data[_id] === id) {
data.splice(_id, 1)
}
};
};
return data
};
XFooProto.ids = function() {
return this.dataset.ids
};
// 2. Define a property read-only "bar".
// Object.defineProperty(XFooProto, "ids", {value: this});
// 3. Register x-foo's definition.
var XFoo = document.registerElement('x-foo', {prototype: XFooProto});
// 4. Instantiate an x-foo.
var xfoo = document.createElement('x-foo');
xfoo.dataset.ids = '["23354", "115866"]';
// 5. Add it to the page.
document.body.appendChild(xfoo);
console.log(xfoo.add("123")); // `["23354", "115866", "123"]`
console.log(xfoo.remove("123")); // `["23354", "115866"]`
console.log(xfoo.contains("123")); // `false`
console.log(xfoo.contains("23354")); // `true`
console.log(xfoo.ids()); // `["23354", "115866"]` , type : `String`
var pre = document.getElementsByTagName("pre")[0]
pre.innerText = JSON.stringify(JSON.parse(xfoo.dataset.ids), null, 4);
<pre></pre>
- 附加到原型属性的Do函数没有闭包
- What does requirejs.config() do?
- 什么是"!函数(){}〃;javascript中的mean/do
- 使用谷歌地图Do’s Donot’s
- 构造一个 DOMTokenList/DOMSettableTokenList 实例
- CoffeeScript do,传递参数
- What does .equals() do?
- Do JS Reference Errors停止运行时线程执行
- Do let语句在全局对象上创建属性
- 什么是“;无效切片大小”;轨道错误?-@categories.ech_slice(column_length)do|co
- do/while语句中的if-elseif语句无限循环javascript
- How to do "HTML FORM action=location.replace('nextp
- Do变量声明初始化函数
- Javascript do/while循环显示意外结果
- 正面或反面游戏javascript do/while循环
- What does connect.facebook.net/en_US/all.js do
- Magento configurable.js - what does separatorIndex do?
- 简单的 do/while 循环无限循环问题
- Do __proto__ 和 Object.getPrototype 的公开完全相同的功能
- JavaScript 中 do-while 循环的用例