2009-05-21 9 views
15

J'essaie de modifier tous les liens sur une page afin qu'ils effectuent un travail supplémentaire quand ils sont cliqués.JavaScript: Ajout d'un gestionnaire onClick sans écraser celui existant

Une approche triviale pourrait être quelque chose comme ceci:

function adaptLinks() 
{ 
    var links = document.getElementsByTagName('a'); 
    for(i = 0; i != links.length; i++) 
    { 
     links[i].onclick = function (e) 
     { 
      <do some work> 
      return true; 
     } 
    } 
} 

Mais certains liens ont déjà un gestionnaire onClickqui doit être préservé. J'ai essayé les éléments suivants:

function adaptLinks() 
{ 
    var links = document.getElementsByTagName('a'); 
    for(i = 0; i != links.length; i++) 
    { 
     var oldOnClick = links[i].onclick; 
     links[i].onclick = function (e) 
     { 
      if(oldOnClick != null && !oldOnClick()) 
      { 
       return false; 
      } 
      <do some work> 
      return true; 
     } 
    } 
} 

Mais cela ne fonctionne pas parce que oldOnClick est évaluée uniquement lorsque le gestionnaire est appelé (il contient la valeur du dernier maillon que ce point).

+0

J'ai vu que de nombreuses tâches similaires Aroun d sont résolus en utilisant jQuery. Je suis actuellement en train d'y jeter un coup d'œil, car il semble également faciliter plusieurs autres tâches. – rluba

Répondre

16

Vous devez créer une fermeture pour préserver la valeur onclick originale de chaque lien:

<a href="#" onclick="alert('hi');return false;">Hi</a> 
<a href="#" onclick="alert('there');return true;">There</a> 
<script type="text/javascript"> 
function adaptLinks() { 
    var links = document.getElementsByTagName('a'); 
    for (i = 0; i != links.length; i++) { 
     links[i].onclick = (function() { 
      var origOnClick = links[i].onclick; 
      return function (e) { 
       if (origOnClick != null && !origOnClick()) { 
        return false; 
       } 
       // do new onclick handling only if 
       // original onclick returns true 
       alert('some work'); 
       return true; 
      } 
     })(); 
    } 
} 
adaptLinks(); 
</script> 

Notez que cette mise en œuvre réalise que la nouvelle manipulation onclick si le gestionnaire onclick d'origine retourne vrai. C'est bien si c'est ce que vous voulez, mais gardez à l'esprit que vous devrez modifier légèrement le code si vous voulez effectuer la nouvelle gestion onclick même si le gestionnaire d'origine renvoie false.

Plus sur les fermetures au comp.lang.javascript FAQ et à partir de Douglas Crockford.

+0

+1 pour l'indice sur les fermetures. Mon exemple a été spécifiquement conçu pour s'exécuter uniquement si le gestionnaire d'origine n'a pas annulé, donc votre exemple convient parfaitement. – rluba

18

Ne pas attribuer à un gestionnaire d'événements directement: utiliser le modèle abonnez-vous addEventListener/attachEvent à la place (qui ont également supprimer paires!).

Bonne introduction here.

+1

+1 pour la page Quirksmode. – outis

+0

Ou, vous pouvez utiliser une librairie javascript (/ moi votes pour jQuery) qui vous permettrait de faire la même chose avec beaucoup plus de facilité. Mais vous devez toujours passer par la théorie à la page quirksmode. C'est très enrichissant pour un développeur javascript. – jrharshath

+1

"Facilité" est relatif. Je peux envelopper ce truc dans une méthode à 5 lignes OU je peux télécharger une bibliothèque multi-K. Une bibliothèque est bonne à avoir, mais pas au détriment de savoir comment faire quelque chose vous-même. Je n'irais pas voter contre jquery, je ne suis pas fan du genou. – annakata

4

Utilisez un wrapper autour de addEventListener (navigateurs prenant en charge DOM) ou attachEvent (IE).

Notez que si vous souhaitez stocker une valeur dans une variable sans écraser l'ancienne valeur, vous pouvez utiliser closures.

function chain(oldFunc, newFunc) { 
    if (oldFunc) { 
    return function() { 
     oldFunc.call(this, arguments); 
     newFunc.call(this, arguments); 
    } 
    } else { 
    return newFunc; 
    } 
} 

obj.method = chain(obj.method, newMethod); 

En Aspect Oriented Programming, cela est connu sous le nom "advice".

0

comment sur le réglage oldClick = links[i].onclick or an empty function. Comme si

var oldOnClick = links[i].onclick || function() { return true; }; 

links[i].onclick = function (e) 
    { 
     if (!oldOnClick()) 
      return false; 
     //<do some work> 
     return true; 
    } 

Ou vous pouvez utiliser attachEvent et addEventListener que d'autres ont recommandé

function addEvent(obj, type, fn) { 
     if (obj.addEventListener) 
       obj.addEventListener(type, fn, false); 
     else if (obj.attachEvent) 
       obj.attachEvent('on' + type, function() { return fn.apply(obj, [window.event]);}); 
} 

et utiliser comme si

addEvent(links[i], 'click', [your function here]); 
+0

Cela ne résout pas le problème que oldOnClick est évalué lorsque le gestionnaire est appelé et non quand il est écrit. Plus: Il ne retourne pas false si l'ancien gestionnaire onclick renvoie false (pour empêcher le lien de fonctionner). – rluba

+0

Ah, je comprends mieux le problème. Ne pourriez-vous pas retourner false si oldOnClick renvoie false? –

0

En utilisant JQuery, le code suivant fonctionne:

function adaptLinks(table, sortableTable) 
{ 
    $('a[href]').click(function (e) 
    { 
     if(!e.isDefaultPrevented()) 
     { 
      <do some work> 
     } 
    }); 
} 

Cela nécessite l'utilisation d'une bibliothèque supplémentaire, mais évite certains problèmes qui existent avec addEventListener/attachEvent (comme le problème de ce dernier avec this references).

Il y a juste un piège: si le gestionnaire onClick d'origine est attribuée à l'aide JavaScript « normal », la ligne

... 
if(!e.isDefaultPrevented()) 
... 

résoudra toujours vrai, même dans le cas où le gestionnaire d'origine a annulé la événement en retournant false. Pour résoudre ce problème, le gestionnaire d'origine doit également utiliser JQuery.

0

Cette fonction doit être utilisable (écouteurs d'événement approche):

function addEventListener(element, eventType, eventHandler, useCapture) { 
    if (element.addEventListener) { 
     element.addEventListener(eventType, eventHandler, useCapture); 
     return true; 
    } else if (element.attachEvent) { 
     return element.attachEvent('on' + eventType, eventHandler); 
    } 
    element['on' + eventType] = eventHandler; 
} 

ou vous pouvez économiser plus de code l'ajout de cette fonction (si vous avez besoin d'ajouter le même écouteur d'événement à de nombreux éléments):

function addClickListener(element) { 
    addEventListener(element, 'click', clickHandler, false); 
} 
Questions connexes