Присоединение сцепленных методов к коллекциям элементов в JavaScript - PullRequest
1 голос
/ 02 марта 2009

Это - по крайней мере на данный момент - чисто экспериментальный, но мне любопытно: есть ли способ прикрепить методы (через прототипирование) к коллекциям элементов? Я проверил следующий код:

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

На данный момент в Опере работает отлично; как и следовало ожидать, метод _ вызывается на всех элементах div, а затем eval () передает строку на каждый элемент по очереди. Обратите внимание, что метод _ позволяет создавать цепочки, и это также было продемонстрировано, вызывая _ для добавления прогнозируемой переменной итератора x к innerHTML каждого элемента.

Теперь два вопроса ...

Во-первых, есть ли лучший способ сделать это? Я очень долго хотел, чтобы я мог просто сделать document.getElementsByTagName('div').style.backgroundColor = "red";, но, увы, этого просто еще не произошло. Вот почему я делаю это в первую очередь, и поэтому я назвал метод так кратко; Я пытаюсь подражать ему как можно ближе.

Во-вторых, если предположить, что это нормальное использование, как бы мне заставить его работать в Firefox? Этот браузер эквивалентен NodeList HTMLCollection, но попытка создать прототип последнего просто не удалась. Предложения?

Ответы [ 2 ]

1 голос
/ 03 марта 2009

Я приготовил то, что, как я полагаю, могло бы остаться жизнеспособным решением; Есть ли что-то принципиально плохое в использовании этого метода для цепной модификации коллекции элементов?

<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"]);
0 голосов
/ 08 декабря 2010

Вот «более симпатичная» версия (без eval, без глобальных, формальных аргументов, без нечеткого кода внутри строк) того, что вам нужно, не устанавливая его в прототипе, потому что это не работает для 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;
}

Контрольный пример Перейдите на google.com с помощью Firefox, введите приведенный выше код в консоль и введите следующее:

// 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";
});
...