2010-09-06 5 views
4

Titre mis à jour pour mieux refléter ce que j'essaie de faire. En résumé, il existe différents constructeurs pour différents éléments dom, et ils ne semblent pas tous partager un prototype commun. Je cherche un moyen d'ajouter une propriété de fonction à chaque élément DOM en modifiant ces prototypes, mais je ne suis pas sûr de la façon de les trouver.Modifier les prototypes de tous les éléments DOM possibles

Par exemple, je pourrais faire quelque chose comme ceci:

function enhanceDom (tagNames, methods) { 
    var i=-1, tagName; 
    while (tagName=tagNames[++i]) { 
    var tag=document.createElement(tagName); 
    if (!(tag && tag.constructor)) continue; 
    for (var methodName in methods) { 
     tag.constructor.prototype[methodName]=methods[methodName]; 
    } 
    } 
} 

var thingsToEnhance = ['a','abbr','acronym','address'/* on and on... */]; 

enhance(thingsToEnhance, { 
    doStuff : function(){ 
    /* ... */ 
    }, 
    doOtherStuff : function(){ 
    /* ... */ 
    } 
    /* ... */ 
}); 

Bien sûr, je voudrais le faire sans énumérer tous les éléments HTML unique. Quelqu'un peut-il penser à un meilleur moyen?

(question originale suit)

Goal - faire getElementsByClassName travailler sur un nœud DOM dans un navigateur.

Cela a déjà été fait (en quelque sorte), mais voici ma photo.

La question que j'ai est, est-il un bon moyen de faire fonctionner cela avec des éléments créés dynamiquement? Il semble que les éléments HTML DOM ne partagent pas un prototype prévisible commun où getElementsByClassName pourrait être ajouté ... Ou est-ce que je manque quelque chose?

Voici ce que j'ai jusqu'à présent (modifier - mise à jour par la discussion).

(function(){ 

    var fn = 'getElementsByClassName'; 
    // var fn = 'gEBCN'; // test 

    if (typeof document[fn] != 'undefined') return; 

    // This is the part I want to get rid of... 
    // Can I add getByClass to a single prototype 
    // somewhere below Object and be done with it? 

    document[fn]=getByClass; 
    withDescendants(document, function (node) { 
    node[fn]=getByClass; 
    }); 

    function withDescendants (node, callback, userdata) { 
    var nodes = node.getElementsByTagName('*'), i=-1; 
    while (node=nodes[++i]) { 
     callback(node, userdata); 
    } 
    return userdata; 
    } 

    function getByClass (className) { 
    return withDescendants(this, getMatches, { 
     query:new RegExp('(^|\\s+)' + className + '($|\\s+)'), 
     found:[] 
    }).found; 
    } 

    function getMatches (node, data) { 
    if (node.className && node.className.match(data.query)) { 
     data.found.push(node); 
    } 
    } 

}()); 

Il fonctionne bien sur le contenu chargé avant le chargement du script, mais de nouveaux éléments dynamiquement créés n'obtenir une méthode getElementsByClassName. Des suggestions (en plus de setInterval, s'il vous plaît)?

+0

Y a-t-il une raison pour laquelle vous avez décidé de ne pas utiliser un framework tel que jQuery pour cela? Si oui, vous pourriez vouloir dire que dans votre question ou vous obtiendrez beaucoup de "utiliser jQuery!" réponses. –

+0

J'espère qu'il sera évident que je n'ai pas l'intention d'utiliser jQuery pour cela. Doigts croisés. –

+1

@Greg Sans code JQuery dans le code affiché et aucune présence de tag JQuery, je pense que c'est suffisant ... –

Répondre

5

Je pense que ce que vous voulez peut être réalisé par le prototypage du Element interface, comme

Element.prototype.getElementsByClassName = function() { 
    /* do some magic stuff */ 
}; 

mais ne le faites pas. Cela ne fonctionne pas de manière fiable dans tous les principaux navigateurs. Ce que vous faites dans l'exemple de votre question n'est pas conseillé non plus. Vous étendez réellement les objets hôtes. Encore une fois, s'il vous plaît ne faites pas cela.

Vous tomberez exactement dans ces pièges Prototype ran into.

Je ne veux pas simplement copier l'article de Kangax, alors veuillez lire What’s wrong with extending the DOM.

Pourquoi voulez-vous cela en premier lieu? Quel est ton but?

+0

Grand article. Je suis conscient de certains des dangers de la modification des objets hôte. L'objectif global de cette expérience est de créer une couche de compatibilité afin que tous les scripts puissent être testés sur un seul navigateur et fonctionner de manière croisée sans modification. Ne pas créer une nouvelle bibliothèque, juste pour étendre les navigateurs qui n'ont pas certaines fonctionnalités de sorte qu'ils fonctionnent tous de la même manière que le navigateur testé par nos développeurs. Il ne serait utilisé que pour les choses internes (dont certaines ont déjà été accumulées), pas comme une bibliothèque GP. –

+0

'Element' ou HTMLElement est probablement ce que je cherchais. Dommage pour le support cross-browser sur celui-là. Marquer cela correct. –

0

Cela semble fonctionner, mais c'est moche. Je me demande si cela fonctionne dans IE?

(function(){ 

    enhanceDom('a abbr acronym address applet area b base basefont bdo big blockquote body br button caption center cite code col colgroup dd del dfn dir div dl dt em fieldset font form frame frameset h1 h2 h3 h4 h5 h6 head hr html i iframe img input ins isindex kbd label legend li link map menu meta noframes noscript object ol optgroup option p param pre q s samp script select small span strike strong style sub sup table tbody td textarea tfoot th thead title tr tt u ul var' 
    ,{ 
    getElementsByClassName : getByClass 
    /* , ... */ 
    }); 

    function enhanceDom (tagNames, methods) { 
    var i=-1, tagName; 
    if (tagNames==''+tagNames) { 
     tagNames=tagNames.split(' '); 
    } 
    for (var methodName in methods) { 
     setIfMissing(document, methodName, methods[methodName]); 
     while (tagName=tagNames[++i]) { 
     var tag=document.createElement(tagName); 
     if (tag || !tag.constructor) continue; 
     var proto=tag.constructor.prototype; 
     setIfMissing(proto, methodName, methods[methodName]); 
     } 
    } 
    } 

    function setIfMissing (obj, prop, val) { 
    if (typeof obj[prop] == 'undefined') { 
     obj[prop]=val; 
    } 
    } 

    function withDescendants (node, callback, userdata) { 
    var nodes=node.getElementsByTagName('*'), i=-1; 
    while (node=nodes[++i]) { 
     callback(node, userdata); 
    } 
    return userdata; 
    } 

    function getByClass (className) { 
    return withDescendants(this, getMatches, { 
     query:new RegExp('(^|\\s+)' + className + '($|\\s+)'), 
     found:[] 
    }).found; 
    } 

    function getMatches (node, data) { 
    if (node.className && node.className.match(data.query)) { 
     data.found.push(node); 
    } 
    } 

}()); 
Questions connexes