2015-06-26 11 views
5

Ho riscontrato il problema con l'inserimento di script di contenuto nella pagina che è stata modificata da history.pushState e una chiamata ajax. Ho trovato lo similar topic allo stackoverflow, ma quella soluzione non ha funzionato per me (quella soluzione era l'uso dell'evento chrome.webNavigation.onHistoryStateUpdated e "popstate").inserire script di contenuto quando la pagina è stata modificata da history.pushState e chiamata ajax

Ecco un frammento del mio manifesto:

"content_scripts": [ 
    { 
     "matches": ["https://vk.com/audios*", "https://vk.com/al_audio.php*"], 
     "js": ["jquery-2.1.4.min.js", "getListOfSongs.js"] 
    } 
    ] 

chrome.webNavigation.onHistoryStateUpdated funziona solo se ci si dirige verso il un'altra pagina, se ci si dirige verso la stessa pagina molte volte in sequenza non succede nulla . Ad esempio: funziona quando

1) Vai https://vk.com/audios * - apertura pagina prima volta o ricaricare

2) Vai https://vk.com/some_other_page - chiamata ajax

3) Vai https://vk.com/audios * - ajax chiamare

non funziona quando

1) Vai alla https://vk.com/audios * - apertura pagina prima volta o ricaricare

2) Anche in questo caso andare al * https://vk.com/audios - chiamata ajax, a questo script contenuto punto non è l'iniezione
3) Anche in questo caso andare al * https://vk.com/audios - chiamata ajax, a questo script contenuto punto non è l'iniezione e così via

Ogni volta che sto facendo clic alla stessa pagina per la seconda volta e così via la seguente richiesta sta generando:

https://vk.com/al_audio.php?__query=audios ********* & _ref = left_nav & _smt = audio% 3A2 & al = -1 & al_id = ******** & _rndVer = 60742

(parametri di richiesta possono variare)

anche JQuery .ajaxComplete non cattura tutti gli eventi in questo caso.

E pushState non scatta evento "popstate", quindi non posso utilizzare evento window.onpopstate

potrei usare chrome.webNavigation.onDOMContentLoaded e chrome.webNavigation.onCompleted ma quando ho ricarica pagina questi eventi accadono più di una volta, quindi lo script verrà iniettato più di una volta.

Qual è la soluzione migliore per questo caso?

+0

_ "I Ho trovato l'argomento simile a StackOverflow, ma quella soluzione non funziona per me "_ Dovrai spiegarlo meglio. – Xan

+1

L'ho spiegato in tutto il post. Utilizzo di chrome.webNavigation.onHistoryStateUpdated e l'evento popstate sono stati suggeriti come soluzione a quell'argomento, ma come ho menzionato sopra questa soluzione non funziona per me. –

+0

Puoi collegare alla soluzione che hai trovato? – Xan

risposta

4

Ci sono due modi possibili mi viene in mente:

1 - Utilizzare i timer per verificare se lo script è ancora lì, se non, aggiungere di nuovo ...
2 - Controllare per le chiamate Ajax e se la loro url corrisponde a uno degli URL che rimuovono il tuo script, aggiungi di nuovo lo script.

Il tuo script (quello definito in manifest) è ancora lì, anche dopo le chiamate Ajax, semplicemente non viene eseguito di nuovo (non è sicuro di cosa succede con il pusher della cronologia). Quindi, presumo sia necessario leggere solo alcuni elementi o rieseguire lo stript. Suppongo che tu aggiunga lo script aggiungendo un tag html.

Quindi quello che ti serve è qualcosa per leggere gli elementi o rieseguire un determinato codice.


1 - Approccio Timer - Ho creato una soluzione per ogni elemento (non solo gli script), che desidero aggiungere ad un certo elemento bersaglio in una pagina.

Utilizza un timer per verificare se l'elemento di destinazione è presente. Quando trova l'elemento di destinazione, aggiunge il mio. Quindi il timer viene regolato per verificare se il mio elemento è ancora lì. In caso contrario, aggiungere di nuovo.

Hai solo bisogno di chiamare appendChildPersistent una sola volta e questo non mancherà di tenere attivo per tutto il tempo a navigare.

var timers = {}; //stores the setInterval ids 

//this is the only method you need to call 
//give your script an `id` (1) 
//the child is your script, it can be anything JQuery.append can take 
//toElem is the Jquery "SELECTOR" of the element to add your script into. 
//I'm not sure what would happen if toElem were not a string. 
//callback is a function to call after insertion if desired, optional. 
appendChildPersistent = function(id, child, toElem, callback) 
{ 
    //wait for target element to appear 
    withLateElement(toElem, function(target) 
    { 
     target.append(child); //appends the element - your script                           
     if (typeof callback !== 'undefined') callback(); //execute callback if any 

     //create a timer to constantly check if your script is still there 
     timers[id] = setInterval(function() 
     {          
      //if your script is not found, clear this timer and tries to add again   
      if (document.getElementById(id) === null) 
      { 
       clearInterval(timers[id]); 
       delete timers[id]; 
       appendChildPersistent(id, child, toElem, callback); 
      } 
     },3000); 

    }); 
} 

//this function waits for an element to appear on the page 
//since you can't foresee when an ajax call will finish 
//selector is the jquery selector of the target element 
//doAction is what to do when the element is found 
function withLateElement(selector, doAction) 
{ 
    //checks to see if this element is already being waited for        
    if (!(selector in timers)) 
    { 
     //create a timer to check if the target element appeared                
     timers[selector] = setInterval(function(){    
      var elem = $(selector); 

      //checks if the element exists and is not undefined 
      if (elem.length >= 0) 
      { 
       if (typeof elem[0] !== 'undefined') 
       { 
        //stops searching for it and executes the action specified 
        clearInterval(timers[selector]); 
        delete timers[selector]; 
        doAction(elem); 
       } 
      } 
     }, 2000); 
    }               
} 

(1) Sembra che non è un problema per aggiungere un ID a un tag script: Giving the script tag an ID


2 - Cattura l'Ajax chiama

Un'opzione è quella di utilizzare chrome.webRequest. Ma stranamente, questo non ha funzionato per me. Un'altra opzione è sotto.

Per questo caso, controllare this answer, e non dimenticare per leggere la risposta relativa a Chrome estensione in là. Funzionerà solo se segui l'intera procedura. Per fortuna, ho provato oggi e funziona benissimo: p

Qui, ciò che si fa è quello di cambiare i metodi XMLHttpRequestopen e send di rilevare (e possibilmente ottenere i parametri troppo) quando sono chiamati.

nell'estensione di Google, tuttavia, è assolutamente necessario che si inietta lo stript nella pagina (non una pagina di sfondo o uno script iniettando lo script contenuti, ma lo script contenuti iniettando del codice nel DOM, come il seguente).

var script = document.createElement('script'); 
script.textContent = actualCode; //actual code is the code you want to inject, the one that replaces the ajax methods 
document.head.appendChild(script); //make sure document.head is already loaded before doing it 
script.parentNode.removeChild(script); //I'm not sure why the original answer linked removes the script after that, but I kept doing it in my solution 

Questo è fondamentale perché l'estensione tenta di creare un ambiente isolato, ei cambiamenti che fate per la XMLHttpRequest in questo ambiente non sarà semplicemente prendere parte. (Ecco perché JQuery.ajaxComplete non sembra funzionare, è necessario iniettare uno script nella pagina per farlo funzionare - look here)

In this pure javascript solution, si sostituiscono i metodi:

//enclosing the function in parentheses to avoid conflict with vars from the page scope 
(function() { 
    var XHR = XMLHttpRequest.prototype; 

    // Store the orignal methods from the request 
    var open = XHR.open; 
    var send = XHR.send; 

    // Create your own methods to replace those 

    //this custom open stores the method requested (get or post) and the url of the request 
    XHR.open = function(method, url) { 
     this._method = method; //this field was invented here 
     this._url = url; //this field was invented here 
     return open.apply(this, arguments); //calls the original method without any change 

     //what I did here was only to capture the method and the url information 
    }; 


    //this custom send adds an event listener that fires whenever a request is complete/loaded 
    XHR.send = function(postData) { 
     //add event listener that fires when request loads 
     this.addEventListener('load', function() { 
      //what you want to do when a request is finished 
      //check if your element is there and readd it if necessary 
      //if you know the exact request url, you can put an if here, but it's not necessary 

      addMyElementsToPage(); //your custom function to add elements 
      console.log("The method called in this request was: " + this._method); 
      console.log("The url of this request was: " + this._url); 
      console.log("The data retrieved is: " + this.responseText); 

     }); 

     //call the original send method without any change 
     //so the page can continue it's execution 
     return send.apply(this, arguments); 

     //what we did here was to insert an interceptor of the success of a request and let the request continue normally 
    }; 
})(); 
+0

Grazie per l'ottima risposta. Ho successo con la prima soluzione. Ma in questo caso sto solo controllando la presenza dell'elemento che ho aggiunto dallo script del contenuto e lo aggiungo nuovamente se questo elemento non esiste. In caso di seconda soluzione, non ho raggiunto il successo a causa della mia esperienza pura con JavaScript. Semplicemente non ho avuto parte con l'override del metodo XMLHttpRequest. Devo modificare XMLHttpRequest originale che viene utilizzato dalla pagina (ad esempio vk.com/audio)? –

+0

Dai un'occhiata alle modifiche. Sostituendo 'send' e' open' da 'XMLHttpReques.prototype' stai effettivamente cambiando il' XMLHttpRequest' originale. Ma non dimenticare la parte ** injection **, è assolutamente necessario in Chrome Extension. Oppure semplicemente non farà nulla. –

Problemi correlati