2009-05-21 17 views
15

Estoy tratando de modificar todos los enlaces en una página para que realicen un trabajo adicional cuando se les haga clic.JavaScript: Agregar un controlador onClick sin sobreescribir el existente

Un enfoque trivial podría ser algo como esto:

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

Pero algunos de los enlaces Ya tiene una onclick que debe ser preservado. He intentado lo siguiente:

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

Pero esto no funcionará porque oldOnClick sólo se evalúa cuando el gestor se llama (que contiene el valor de la última enlace como este punto).

+0

que he visto que muchas tareas similares todas partes del mundo d aquí se resuelven usando jQuery. Actualmente estoy investigando, ya que parece facilitar muchas otras tareas también. – rluba

Respuesta

16

lo necesario para crear un cierre para preservar el valor original onclick de cada enlace:

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

Tenga en cuenta que esta aplicación sólo lleva a cabo el nuevo onclick manejo si el manejador onclick originales devuelve verdadero. Eso está bien si eso es lo que quiere, pero tenga en cuenta que tendrá que modificar el código ligeramente si desea realizar el nuevo manejo onclick, incluso si el controlador original devuelve falso.

Más sobre cierres en comp.lang.javascript FAQ y Douglas Crockford.

+0

+1 para la sugerencia de cierres. Mi ejemplo fue diseñado específicamente para ejecutarse solo si el controlador original no se canceló, por lo que su ejemplo se adapta muy bien. – rluba

18

no se asigna a un controlador de eventos directamente: utilice el modelo de suscripción addEventListener/attachEvent lugar (que también tiene eliminar los pares!).

Buena introducción here.

+1

+1 para la página Quirksmode. – outis

+0

O bien, podría utilizar una biblioteca javascript (/ me votes para jQuery) que le permita hacer lo mismo con mucha más facilidad. Pero aún debes seguir con la teoría en la página de modo peculiar. Es bastante enriquecedor para un desarrollador de JavaScript. – jrharshath

+1

"Facilidad" es relativo. Puedo envolver todo esto en un método de 5 líneas O puedo descargar una biblioteca multi-K. Una biblioteca es buena, pero no a expensas de saber cómo hacer algo tú mismo. No rechazaría votar a jquery, simplemente no estoy interesado en el kneejerk. – annakata

4

Utilice un contenedor alrededor de addEventListener (navegadores compatibles con DOM) o attachEvent (IE).

Tenga en cuenta que si alguna vez desea almacenar un valor en una variable sin sobreescribir el valor anterior, puede usar 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, esto se conoce como "advice".

0

cómo sobre configuración oldClick = links[i].onclick or an empty function. Al igual que

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

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

O usted podría utilizar attachEvent y addEventListener como otros han recomendado

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]);}); 
} 

y utilizar como tal

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

No resuelve el problema de que oldOnClick se evalúa cuando se llama al controlador y no cuando se escribe. Además: no devuelve falso si el manejador onclick anterior devuelve falso (para evitar que el enlace funcione). – rluba

+0

Ah, entiendo el problema mejor. ¿No podría devolver falso si oldOnClick devuelve falso? –

0

usando jQuery, el siguiente código funciona:

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

Esto requiere el uso de una biblioteca adicional pero evita algunos problemas que existen con addEventListener/attachEvent (como el problema de este último con this references).

Sólo hay una trampa: si el controlador original de onClick se le asigna el uso de JavaScript "normal", la línea

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

siempre se resolverá a cierto, incluso en caso de que el controlador original, canceló la evento al devolver falso. Para solucionar esto, el controlador original también debe usar JQuery.

0

Esta función debe ser utilizable (detectores de eventos se acercan a):

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

o se puede ahorrar un poco más código añadir esta función (si es necesario agregar el mismo detector de eventos para muchos elementos):

function addClickListener(element) { 
    addEventListener(element, 'click', clickHandler, false); 
} 
Cuestiones relacionadas