2009-03-02 8 views
1

C'est du moins pour le moment une pure expérimentation, mais je suis curieux: existe-t-il un moyen d'attacher des méthodes (via le prototypage) à des collections d'éléments? J'ai testé le code suivant:Attacher des méthodes chaînées à des collections d'éléments en JavaScript

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

À l'heure actuelle, il fonctionne parfaitement dans Opera; Tout comme on peut s'y attendre, la méthode _ est appelée sur tous les éléments div, puis eval() la chaîne qui lui est passée sur chaque élément à son tour. Notez que la méthode _ permet le chaînage, et cela a été démontré, en appelant _ pour ajouter la variable d'itération x prédite au innerHTML de chaque élément.

Maintenant, deux questions ...

d'abord, est-il une meilleure façon d'aller à ce sujet? Je ai pour le plus longtemps souhaité que je pouvais simplement faire document.getElementsByTagName('div').style.backgroundColor = "red";, mais hélas, il n'a tout simplement pas encore été. C'est pourquoi je le fais en premier lieu, et pourquoi j'ai nommé la méthode si succinctement; J'essaie de l'imiter le plus possible. Deuxièmement, en supposant que ce soit un bon usage, comment pourrais-je le faire fonctionner dans Firefox? L'équivalent de NodeList de ce navigateur est HTMLCollection, mais essayer de prototyper ce dernier ne fonctionne tout simplement pas. Suggestions?

Répondre

1

J'ai fait cuire ce que je suppose pourrait rester comme une solution viable; Y at-il quelque chose de fondamentalement mauvais à propos de l'utilisation de cette méthode pour modifier en chaîne une collection d'éléments?

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

Utilisation:

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

Oui, vous n'avez pas besoin eval pour cela. Etant donné que vous n'avez réellement que 2 arguments, pourquoi ne pas les nommer et utiliser arguments [] array? – BYK

0

Voici une version 'plus jolie' (pas eval, pas globals, arguments formels, code fugly à l'intérieur des chaînes) de ce que vous avez besoin, la mise pas sur le prototype car cela ne fonctionne pas pour 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 Aller à google.com en utilisant Firefox, tapez le code ci-dessus dans la console, puis tapez ce qui suit:

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

MISE À JOUR Ma solution a gagné » t travailler si vous voulez être capable de faire + = comme vous l'avez mentionné. La solution la plus élégante que je pense est d'utiliser une boucle de rappel comme celle-ci.

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

Ensuite, vous pouvez l'appeler comme ça

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