2011-11-26 21 views
27

Estoy trabajando en un script de usuario y acabo de descubrir que el script no se ejecuta cuando la página principal realiza solicitudes AJAX.Script Fire Greasemonkey en la solicitud AJAX

¿Hay alguna forma de activar el script del usuario tanto en la carga de la página principal como en las solicitudes AJAX?

Respuesta

56

La forma inteligente de volver a ejecutar el código del script en las solicitudes AJAX, es centrarse en los bits clave de la página y comprobar si hay cambios.

Por ejemplo, supongamos que una página contenida HTML así:

<div id="userBlather"> 
    <div class="comment"> Comment 1... </div> 
    <div class="comment"> Comment 2... </div> 
    ... 
</div> 

y que querían que la secuencia de comandos para hacer algo con cada comentario, ya que se produjo en

Ahora podía interceptar todas AJAX. llama al o escucha por DOMSubtreeModified (obsoleto), o usa MutationObserver s, pero estos métodos pueden ser complicados, complicados y demasiado complicados.

Una manera más simple y más robusta de obtener un contenido javascript en una página comodín es sondearlo usando algo como la función waitForKeyElements, a continuación.

Por ejemplo, este script resaltar los comentarios que contengan "cerveza", ya que en AJAX:

// ==UserScript== 
// @name   _Refire on key Ajax changes 
// @include   http://YOUR_SITE.com/YOUR_PATH/* 
// @require   http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js 
// ==/UserScript== 

function highlightGoodComments (jNode) { 

    //***** YOUR CODE HERE ***** 

    if (/beer/i.test (jNode.text())) { 
     jNode.css ("background", "yellow"); 
    } 
    //... 
} 
waitForKeyElements ("#userBlather div.comment", highlightGoodComments); 

/*--- waitForKeyElements(): A utility function, for Greasemonkey scripts, 
    that detects and handles AJAXed content. 

    IMPORTANT: This function requires your script to have loaded jQuery. 
*/ 
function waitForKeyElements (
    selectorTxt, /* Required: The jQuery selector string that 
         specifies the desired element(s). 
        */ 
    actionFunction, /* Required: The code to run when elements are 
         found. It is passed a jNode to the matched 
         element. 
        */ 
    bWaitOnce,  /* Optional: If false, will continue to scan for 
         new elements even after the first match is 
         found. 
        */ 
    iframeSelector /* Optional: If set, identifies the iframe to 
         search. 
        */ 
) { 
    var targetNodes, btargetsFound; 

    if (typeof iframeSelector == "undefined") 
     targetNodes  = $(selectorTxt); 
    else 
     targetNodes  = $(iframeSelector).contents() 
              .find (selectorTxt); 

    if (targetNodes && targetNodes.length > 0) { 
     btargetsFound = true; 
     /*--- Found target node(s). Go through each and act if they 
      are new. 
     */ 
     targetNodes.each (function() { 
      var jThis  = $(this); 
      var alreadyFound = jThis.data ('alreadyFound') || false; 

      if (!alreadyFound) { 
       //--- Call the payload function. 
       var cancelFound  = actionFunction (jThis); 
       if (cancelFound) 
        btargetsFound = false; 
       else 
        jThis.data ('alreadyFound', true); 
      } 
     }); 
    } 
    else { 
     btargetsFound = false; 
    } 

    //--- Get the timer-control variable for this selector. 
    var controlObj  = waitForKeyElements.controlObj || {}; 
    var controlKey  = selectorTxt.replace (/[^\w]/g, "_"); 
    var timeControl  = controlObj [controlKey]; 

    //--- Now set or clear the timer as appropriate. 
    if (btargetsFound && bWaitOnce && timeControl) { 
     //--- The only condition where we need to clear the timer. 
     clearInterval (timeControl); 
     delete controlObj [controlKey] 
    } 
    else { 
     //--- Set a timer, if needed. 
     if (! timeControl) { 
      timeControl = setInterval (function() { 
        waitForKeyElements ( selectorTxt, 
              actionFunction, 
              bWaitOnce, 
              iframeSelector 
             ); 
       }, 
       300 
      ); 
      controlObj [controlKey] = timeControl; 
     } 
    } 
    waitForKeyElements.controlObj = controlObj; 
} 

Actualización:

Para mayor comodidad, waitForKeyElements() es ahora hosted on GitHub.

This answer shows an example of how to use the hosted function.

+0

I discúlpate, los errores en realidad se encuentran en zeroclipboard, no en tu función. Una cosa interesante que descubrí durante la depuración es que si 'bWaitOnce' se establece en' true' el código realmente se dispara dos veces, no estoy seguro si fue intencional o no – RozzA

+0

@RozzA, me gustaría ver un código que así lo demuestre. Partes del código pueden dispararse dos veces (o muchas veces) por diseño. Pero la 'actionFunction' solo debería disparar una vez por cada nodo dado. –

+0

@BrockAdams, ¿por qué 'setInterval' y no' MutationObserver'? –

0

Otra forma, más simple y más pequeña pero menos flexible, es utilizar un retraso de tiempo de JavaScript para esperar a que AJAX/jQuery cargue y termine. Por ejemplo, si el siguiente código HTML se genera dinámicamente después de la primera carga:

<div id="userBlather"> 
    <div class="comment"> Comment 1... </div> 
    <div class="comment"> Comment 2... </div> 
    ... 
</div> 

A continuación, un script de Greasemonkey como esto sería capaz de modificarlo:

// Wait 2 seconds for the jQuery/AJAX to finish and then modify the HTML DOM 
window.setTimeout(updateHTML, 2000); 

function updateHTML() 
{ 
    var comments = document.getElementsByClassName("comment"); 
    for (i = 0; i < comments.length; i++) 
    { 
     comments[i].innerHTML = "Modified comment " + i; 
    } 
} 

Ver guía aquí: Sleep/Pause/Wait in Javascript

Cuestiones relacionadas