构造DOMTokenList / DOMSettableTokenList实例 [英] Constructing a DOMTokenList/DOMSettableTokenList instance

查看:102
本文介绍了构造DOMTokenList / DOMSettableTokenList实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

DOMTokenList和DOMSettableTokenList接口( MDN WHATWG )提供了用于操作由空格分隔的字符串表示的字符串令牌的有序集的方法。它们通常以Element.prototype.classList属性的形式使用,DOMTokenList反映相关元素的属性。



  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 .contain('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('所以用户')。setAttribute('ids','23354 115866'); 

而不是要求用户直接操作这个属性,我想要一个 .ids 属性提供一个可以使用的DOMTokenList。理想情况下,这将直接与属性相关联,但是我必须手动绑定的未绑定的DOMSettableTokenList实例也是可以的。

  document.querySelector( '所以用户')ids.add( '17174'); 

不幸的是,我一直无法找到创建DOMTokenList实例的任何方法。定义不是一个构造函数,当我调用任何关联的方法时,使用它的原型直接创建一个对象会导致错误:

  new DOMTokenList; // TypeError:非法构造函数
new DOMSettableTokenList; // TypeError:非法构造函数



  var list = Object.create(DOMSettableTokenList.prototype,{
value:{value:'hello world'}
});
console.assert(list instanceof DOMTokenList);
console.assert(list instanceof DOMSettableTokenList);
list.item(0); // TypeError:非法调用



  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:非法调用

如何构建一个新的 DOMTokenList DOMSettableTokenList 实例?

解决方案

一个DOMTokenList或一个DOMSettableTokenList直接。相反,您应该使用class属性来存储和检索您的数据,也可能将DOM元素的ids属性映射到classList属性。

  var element = document.querySelector('so-users'); 
element.ids = element.classList;

您可以根据文档使用relList,但是更多支持classList,唯一的缺点是你可能遇到问题,如果其中一个ids匹配一个类名称,那么设置一个内联样式来隐藏元素以防万一。



对于定制组件的兼容性应该是一个问题classList存在于IE> = 10,Firefox 3.6,Chrome 8,Opera 11.5和Safari 5.1中,请参阅 http:/ /caniuse.com/#feat=classlist ),所以如果兼容性符合您的要求,请使用下面发布的另一个解决方案。



如果不能使用clases或classList和/或必须使用ids属性,您应该根据规范实现一个自定义函数,具有以下属性作为函数。




  • item()

  • contains()

  • add()

  • remove()

  • toggle()



这是一个示例引用这样的功能。

  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':提供的令牌(' + search +')包含HTML空格字符,这些字符在令牌中无效。););
} else {
cb(search);
}
};

函数triggerAttributeChange(){
if(self.tokenChanged&&&&" 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)!== ;
});
};

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 tokens = tokens + idsArray [i] +''
}
tokens = tokens.slice(0,tokens.length - 1);
}
返回令牌;
};
};

使用此函数的新实例在元素中设置ids属性,最后必须绑定对属性的目标属性监听元素的更改并更新属性o反向。你可以用突变观察器来做到这一点。



请参阅在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;

函数createTokenList(values){
var tList = new TokenList(values);
tList.tokenChanged = function(){
element.setAttribute(prop,element [prop] .toString());
cancelMutation = true;
};
元素[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 = {
属性:true
};
observer.observe(element,config);
};

测试看是否可行

 < so-users ids =1234 5678>< / so-users> 
< button onclick =clickButton1()>添加7890< / button>
< button onclick =clickButton2()>设置为3456< / button>
< button onclick =clickButton3()>添加9876< / button>

脚本标签内

  var elem = document.querySelector('so-users'); 
attachTokenList(elem,'ids')

function clickButton1(){
elem.ids.add('7890');
}

function clickButton2(){
elem.setAttribute('ids','3456');
}

功能clickButton3(){
elem.ids.add('9876');
}

按顺序单击按钮将ids属性设置为'3456 9876' p>

The DOMTokenList and DOMSettableTokenList interfaces (MDN, WHATWG) provide methods for manipulating ordered sets of string tokens represented by space-delimited strings. They are most commonly used in the form of the Element.prototype.classList property, a DOMTokenList which reflects the class attribute of an associated element.

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');

I'm working on a custom element (HTML5Rocks, W3C Draft) which displays a real-time feed of the activity of specified Stack Overflow users. This list of users is specified in an ids attribute, and may be updated at any time.

<so-users ids="1114 22656 106224"></so-users>

document.querySelector('so-users').setAttribute('ids', '23354 115866');

Instead of requiring users to manipulate this attribute directly, I would like to have an .ids property providing a DOMTokenList that they can use instead. Ideally this would be directly associated with the attribute, but an unbound DOMSettableTokenList instance that I have to manually bind would also be fine.

document.querySelector('so-users').ids.add('17174');

Unfortunately, I have been unable to find any way to create a DOMTokenList instance. The definition is not a constructor, and directly creating an object using its prototype results in errors when I call any associated methods:

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

How can I construct a new DOMTokenList or DOMSettableTokenList instance?

解决方案

You cannot create an DOMTokenList or an DOMSettableTokenList directly. Instead you should use the class attribute to store and retrieve your data and perhaps map an ids attribute of your DOM element to the classList property.

    var element = document.querySelector('so-users');
    element.ids = element.classList;

You can use relList according to the documentation but classList is more supported, the only drawback is that you might run into issues if one of your ids matches a class name so set an inline style to hide the element just in case.

For a custom component compatibility should be a concern (classList is present in IE>=10, Firefox 3.6, Chrome 8, Opera 11.5 and Safari 5.1, see http://caniuse.com/#feat=classlist) so if compatibility is in your requirements use the another solution posted below.

If you cannot use clases or classList and/or must use the ids attribute you should implement a custom function according to the spec with the following properties as functions.

  • item()
  • contains()
  • add()
  • remove()
  • toggle()

This is an example implementation of such functionality.

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;
    };
};

Set an 'ids' property in your element with a new instance of this function and finally you must bound the targeted attribute to the property listening to changes to the element and updating the property o viceversa. You can do that with a mutation observer.

See firing event on DOM attribute change and 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);
};

Testing to see if it works

<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>

Inside a script tag

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');
}

Clicking the buttons in sequence set the ids attribute to '3456 9876'

这篇关于构造DOMTokenList / DOMSettableTokenList实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆