将链式方法附加到 JavaScript 中的元素集合

发布于 2024-07-14 08:21:16 字数 944 浏览 10 评论 0原文

至少目前来说,这纯粹是实验,但我很好奇:有没有办法将方法(通过原型设计)附加到元素集合? 我测试了以下代码:

<div>a</div>
<div>b</div>
<div>c</div>
<script>
NodeList.prototype._ = function(s)
 {
    for (x = 0; x < this.length; x++)
     {
        eval('this[x]' + '.' + s);
     }
    return this;
 }
document.getElementsByTagName('div')._("style.backgroundColor = 'red'")._('innerHTML += x');
</script>

目前,它在 Opera 中运行得很好; 正如预期的那样,在所有 div 元素上调用 _ 方法,然后将传递给它的字符串依次 eval() 到每个元素上。 请注意, _ 方法允许链接,并且这也已被演示,调用 _ 将预测的 x 迭代器变量附加到每个元素的innerHTML。

现在,有两个问题......

首先,有没有更好的方法来解决这个问题? 我一直希望能够做到 document.getElementsByTagName('div').style.backgroundColor = "red";,但可惜的是,它还没有实现。 这就是我首先这样做的原因,也是我如此简洁地命名该方法的原因; 我正在尝试尽可能地模仿它。

其次,假设这是一个合理的用法,我将如何让它在 Firefox 中工作? 该浏览器相当于 NodeList 的是 HTMLCollection,但尝试对后者进行原型设计根本不会成功。 建议?

This is—at least at the moment—purely experimentation, but I'm curious: is there a way to attach methods (via prototyping) to collections of elements? I've tested the following code:

<div>a</div>
<div>b</div>
<div>c</div>
<script>
NodeList.prototype._ = function(s)
 {
    for (x = 0; x < this.length; x++)
     {
        eval('this[x]' + '.' + s);
     }
    return this;
 }
document.getElementsByTagName('div')._("style.backgroundColor = 'red'")._('innerHTML += x');
</script>

At the moment, it works perfectly in Opera; just as would be expected, the _ method is being called on all of the div elements, and then eval()'ing the string passed to it onto each element in turn. Note that the _ method allows for chaining, and that's been demonstrated as well, calling _ to append the predicted x iterator variable to the innerHTML of each element.

Now, two questions...

First, is there a better way of going about this? I have for the longest wished I could just do document.getElementsByTagName('div').style.backgroundColor = "red";, but alas, it simply hasn't yet come to be. This is why I am doing this in the first place, and why I named the method so succinctly; I'm trying to emulate it as closely as possible.

Secondly, assuming this is a sane usage, how would I go about getting it to work in Firefox? That browser's equivalent of NodeList is HTMLCollection, but trying to prototype the latter simply doesn't succeed. Suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

难如初 2024-07-21 08:21:16

我已经制定了我认为可以保留的可行解决方案; 使用这种方法来链式修改元素集合有什么根本上的坏处吗?

<script>
_ = function()
 {
    for (x = 0; x < arguments[0].length; x++)
     {
        for (y = 0; y < arguments[1].length; y++)
         {
            eval('arguments[0][x]' + '.' + arguments[1][y]);
         }
     }
 }
</script>

用法:

divs = document.getElementsByTagName('div');
_(divs, ["style.color = 'red'", "innerHTML += x"]);

I've cooked up what I suppose could stay as a viable solution; is there anything fundamentally bad about using this method to chain-modify a collection of elements?

<script>
_ = function()
 {
    for (x = 0; x < arguments[0].length; x++)
     {
        for (y = 0; y < arguments[1].length; y++)
         {
            eval('arguments[0][x]' + '.' + arguments[1][y]);
         }
     }
 }
</script>

Usage:

divs = document.getElementsByTagName('div');
_(divs, ["style.color = 'red'", "innerHTML += x"]);
风筝有风,海豚有海 2024-07-21 08:21:16

这是您需要的“更漂亮”版本(没有评估,没有全局变量,形式参数,字符串内没有丑陋的代码),不要将其设置在原型上,因为这不适用于 IE。

/**
 * Sets a property on each of the elements in the list
 * @param {NodeList} list
 * @param {string} prop The name of property to be set, 
 *        e.g., 'style.backgroundColor', 'value'.
 * @param {mixed} value what to set the value to
 */
function setListProp( list, prop, value) {    
    for (var i = 0; i < list.length; i++) {
        setProp(list[i], prop, value);
    }
}

/**
 * Avoids the use of eval to set properties that may contain dots
 * Why avoid eval? eval is slow and could be dangerous if input comes from 
 * an unsanitized source
 * @param {object} el object that will have its property set
 * @param {string} propName ('value', 'style.backgroundColor')
 * Example: setProp(node, 'style.backgroundColor', "#ddd");
 */
function setProp(el, propName, value) {
    var propList = propName.split('.');
    // Note we're not setting it to the last value in the property chain
    for (var i=0; i < propList.length - 1 ; i++) {
        el = el[propList[i]];
    }
    var lastProperty = propList[propList.length -1];
    el[lastProperty] = value;
}

测试用例
使用 Firefox 访问 google.com,在控制台中输入上述代码,然后输入以下内容:

// Set tooltip on links
setListProp( document.getElementsByTagName('a'), 'title', 'YEAH it worked');


// Set bg to red on all links
setListProp( document.getElementsByTagName('a'), 'style.backgroundColor', '#f00');

UPDATE
如果您希望能够像您提到的那样执行 += 操作,我的解决方案将不起作用。 我认为最优雅的解决方案是使用如下所示的回调循环。

/** 
 * This exists in many libs and in newer versions of JS on Array's prototype 
 * @param {Object[]} arr The array that we want to act on each element. 
 *                   Does not work for sparse arrays
 * @param {Function} callback The function to be called for each element, it will be passed
 *        the element as its first argument, the index as the secibd
 */
function iterate(arr, callback) {
  for (var i=0,item; item=arr[i]; i++) {
    callback(item, i);
  }
}

然后你可以这样称呼它

var as = document.getElementsByTagName('a'); 
iterate( as, function(el, index) {
  el.style.backgroundColor = 'red';
  el.innerHTML += "Whatever";
});

Here's a 'prettier' version (no eval, no globals, formal arguments, no fugly code inside strings) of what you need, not setting it on the prototype because that doesn't work for IE.

/**
 * Sets a property on each of the elements in the list
 * @param {NodeList} list
 * @param {string} prop The name of property to be set, 
 *        e.g., 'style.backgroundColor', 'value'.
 * @param {mixed} value what to set the value to
 */
function setListProp( list, prop, value) {    
    for (var i = 0; i < list.length; i++) {
        setProp(list[i], prop, value);
    }
}

/**
 * Avoids the use of eval to set properties that may contain dots
 * Why avoid eval? eval is slow and could be dangerous if input comes from 
 * an unsanitized source
 * @param {object} el object that will have its property set
 * @param {string} propName ('value', 'style.backgroundColor')
 * Example: setProp(node, 'style.backgroundColor', "#ddd");
 */
function setProp(el, propName, value) {
    var propList = propName.split('.');
    // Note we're not setting it to the last value in the property chain
    for (var i=0; i < propList.length - 1 ; i++) {
        el = el[propList[i]];
    }
    var lastProperty = propList[propList.length -1];
    el[lastProperty] = value;
}

Test Case
Go to google.com using Firefox, type the above code into the console, then type the following:

// Set tooltip on links
setListProp( document.getElementsByTagName('a'), 'title', 'YEAH it worked');


// Set bg to red on all links
setListProp( document.getElementsByTagName('a'), 'style.backgroundColor', '#f00');

UPDATE
My solution won't work if you want to be able to do += as you mentioned. The most elegant solution I think is to use a callback loop like the following.

/** 
 * This exists in many libs and in newer versions of JS on Array's prototype 
 * @param {Object[]} arr The array that we want to act on each element. 
 *                   Does not work for sparse arrays
 * @param {Function} callback The function to be called for each element, it will be passed
 *        the element as its first argument, the index as the secibd
 */
function iterate(arr, callback) {
  for (var i=0,item; item=arr[i]; i++) {
    callback(item, i);
  }
}

Then you can call it like this

var as = document.getElementsByTagName('a'); 
iterate( as, function(el, index) {
  el.style.backgroundColor = 'red';
  el.innerHTML += "Whatever";
});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文