插入内容脚本时,页面由history.pushState和Ajax调用改变 [英] insert content script when page was changed by history.pushState and ajax call

查看:273
本文介绍了插入内容脚本时,页面由history.pushState和Ajax调用改变的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我面临着与插入的内容脚本到这是由history.pushState和Ajax调用改变了页面的问题。我已经找到了<一href="http://stackoverflow.com/questions/13806307/how-to-insert-content-script-in-google-chrome-extension-when-page-was-changed-vi">similar话题的计算器,但解决方案并没有为我工作(该解决方案是使用chrome.webNavigation.onHistoryStateUpdated和popstate事件)。

I've faced with the problem with inserting content script into the page which was changed by history.pushState and ajax call. I've found the similar topic at stackoverflow, but that solution doesn't work for me (that solution was in using of chrome.webNavigation.onHistoryStateUpdated and "popstate" event ).

下面是我表现的一个片段:

Here is a fragment of my manifest:

"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 只有当我浏览到另一个页面,如果我浏览到同一页面多次在序列 什么都没发生。例如:它的工作原理,当

chrome.webNavigation.onHistoryStateUpdated works only if i navigate to the another page, if i navigate to the same page many times in sequence nothing happens. For example: it works when

1)转至 https://vk.com/audios * - 打开页面第一次还是重装

1) Go to the https://vk.com/audios* - opening page first time or reloading

2)转到 https://vk.com/some_other_page - Ajax调用

2) Go to the https://vk.com/some_other_page - ajax call

3)进入 https://vk.com/audios * - Ajax调用

3) Go to the https://vk.com/audios* - ajax call

它不工作时,

1)转至 https://vk.com/audios * - 打开页面第一次还是重装

1) Go to the https://vk.com/audios* - opening page first time or reloading

2)再次进入 https://vk.com/audios * - AJAX调用,在这点内容脚本没有注射
3)再次进入 https://vk.com/audios * - AJAX调用,在这一点上的内容脚本是不是注射等

2) Again go to the https://vk.com/audios* - ajax call, at this point content script isn't injecting
3) Again go to the https://vk.com/audios* - ajax call, at this point content script isn't injecting and so on

每当我点击同一页面第二次,因此在以下的请求生成:

Every time i am clicking to the same page for the second time and so on the following request is generating:

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

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

(的要求可能会有所不同参数)

(parameters of request may vary)

另外的 JQuery的.ajaxComplete 不赶在这种情况下,任何事件。

Also JQuery .ajaxComplete doesn't catch any events in this case.

和pushState不火popstate事件,所以我不能用window.onpopstate事件

And pushState doesn't fire "popstate" event, so i can't use window.onpopstate event

我可能会使用 chrome.webNavigation.onDOMContentLoaded chrome.webNavigation.onCompleted 但是当我重新加载这些事件都发生多于一次页面,所以脚本会被注入一个以上的时间。

I might use chrome.webNavigation.onDOMContentLoaded and chrome.webNavigation.onCompleted but when i reload page these events are happening more then one time, so script will be injected more than one time.

,这是什么情况下,最好的解决办法?

What is the best solution for this case?

推荐答案

有两种可能的方式我能想到的:

There are two possible ways I can think of:

1 - 使用定时器来检查,如果你的脚本仍然存在,如果没有,再添加...
2 - 检查Ajax调用,如果他们的URL匹配删除脚本中有一个网址,再次添加脚本

1 - Use timers to check if your script is still there, if not, add again...
2 - Check for ajax calls and if their url matches one of the urls that remove your script, add the script again.

您的脚本(在清单中定义的)仍然存在,甚至在Ajax调用,它只是不再次运行(不知道的历史推进会发生什么)。所以,我假设你只是需要重新进行添加一些元素或重新运行stript。我以为你添加脚本附加HTML标记。

Your script (the one defined in manifest) is still there, even after the ajax calls, it just doesn't run again (not sure what happens with the history pusher). So, I'm assuming you need to just readd some elements or rerun the stript. I supposed you adding the script appending an html tag.

那么,你需要的是要重新进行添加元素或重新运行某个code。

So what you need is something to readd elements or rerun a certain code.

1 - 定时器的做法 - 我创建了一个解决方案的任何元素(不仅是脚本),我想补充一定的目标元素在页面中。

1 - Timer approach - I created a solution for any element (not only scripts) that I wish to add to a certain target element in a page.

它使用一个计时器来检查目标元素是present。 当它发现了目标元素,它增加了我的。然后,计时器被调整到检查,如果我的元素仍然存在。如果没有,重新添​​加。

It uses a timer to check if the target element is present. When it finds the target element, it adds mine. Then the timer is adjusted to check if my element is still there. If not, add again.

您只需要调用 appendChildPersistent 单的时候,这将继续保持活跃所有您浏览的时间。

You just need to call appendChildPersistent a single time and this will keep active all the time you navigate around.

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)看来这不是一个问题,一个ID添加到一个脚本标签:的给script标签的ID

2 - 捕获Ajax调用

2 - Capture the ajax calls

这是一种选择是使用 chrome.webRequest 。但奇怪的是,这并没有为我工作。另一种选择是以下

An option is to use chrome.webRequest. But strangely, this didn't work for me. Another option is below.

对于这种情况,请检查这个答案,和唐忘记阅读相关答案为 Chrome浏览器扩展在那里。如果按照整个程序将只工作。幸运的是,我今天测试了它和它的伟大工程:P

For this case, check this answer, and don't forget to read the related answer to Chrome extension in there. It will only work if you follow the entire procedure. Fortunately, I tested it today and it works great :p

在这里,你做的是改变 XMLHtt prequest 方法打开发送来检测(和可能获得的参数也是如此),当它们被称为。

Here, what you do is to change the XMLHttpRequest methods open and send to detect (and possibly get the parameters too) when they are called.

在谷歌推广,但是,这是绝对必要的,你的页面(不是后台页面或脚本注入的内容脚本,注入stript但内容的脚本注入一些code 到DOM ,像下面的)。

In the Google Extension, however, it's absolutely necessary that you inject the stript in the page (not a background page or script injecting your content script, but your content script injecting some code into the dom, like the following).

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

这是至关重要的,因为扩展试图创建一个孤立的环境,在这种环境下,你做了 XMLHtt prequest 的变化根本不会参加。 (这就是为什么JQuery.ajaxComplete似乎不工作,你需要在页面注入一个脚本,它的工作 - <一个href="http://stackoverflow.com/questions/9515704/building-a-chrome-extension-inject-$c$c-in-a-page-using-a-content-script/9517879#9517879">look这里)

This is crucial because the extension tries to create an isolated environment, and the changes you do to the XMLHttpRequest in this environment will simply not take part. (That's why JQuery.ajaxComplete doesn't seem to work, you need to inject a script in the page for it to work - look here)

这种纯JavaScript的解决方案,则更换方法:

//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
    };
})();

这篇关于插入内容脚本时,页面由history.pushState和Ajax调用改变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆