单击按钮以允许网页在点击之间进行处理

时间:2017-08-22 02:43:21

标签: javascript google-chrome-extension

我正在尝试自动化我的页面测试用例,以便用户能够从Chrome扩展程序按下按钮(更新)以及每个按钮匹配我想要的网页上的类,一次一个,循环它们通过:

  1. 更新:匹配:class="updateButton"。需要点击一次。点击后,网页会在某个时刻将按钮转换为“保存”状态。
  2. 保存:匹配:class="saveButton"。需要点击一次。点击后,网页会在某个时刻将按钮转换为“已保存”状态。
  3. 已保存:当按钮到达此处时,我们已完成此按钮。
  4. 每个按钮进入“已保存”状态后,我们可以转到下一个按钮。我想将所有匹配的按钮处理到“已保存”状态。

    我很亲密。但是,当我得到document.getElementsByClassName("saveButton")的数组长度时,它会一直返回1,这很奇怪。用户点击保存后应返回0。

    的manifest.json

    {
        "manifest_version": 2,
        "name": "My Chrome extension",
        "description": "My Chrome extension",
        "version": "1.0",
        "permissions": [
            "tabs",
            "https://mywebsite.com/*",
            "storage"
        ],
        "browser_action": {
            "default_icon": {
                "30": "images/icons/30.png",
                "48": "images/icons/48.png"
            },
            "default_popup": "popup.html"
        },
        "icons": {
            "16": "images/icons/16.png",
            "20": "images/icons/20.png",
            "30": "images/icons/30.png",
            "48": "images/icons/48.png",
            "128": "images/icons/128.png"
        }
    }
    

    content_script.js:

    function sleep(milliseconds) {
        var start = new Date().getTime();
        for (var i = 0; i < 1e7; i++) {
            if ((new Date().getTime() - start) > milliseconds) {
                break;
            }
        }
    }
    
    function clickUpdate() {
        var updateArray = document.getElementsByClassName("updateButton");
        var updateArraySelector = document.querySelectorAll(".updateButton");
        var saveArray = document.getElementsByClassName("saveButton");
        var delay = 0;
        setSelectedValue(objSelect, "P");
        for (var a = 0; a < updateArraySelector.length; a++) {
            console.log("for loop started...")
            sleep(delay);
            console.log("this is the save array length after 1 second delay top: " + saveArray.length);
            //for each update button in the array do something..    
            //if the there is nothing in the saveArray 
            if (!saveArray.length) {
                updateArraySelector[a].click();
                sleep(delay);
                console.log("update clicked!" + a);
                for (var b = 0; b < saveArray.length; b++) {
                    saveArray[b].click();
                    sleep(delay);
                }
                sleep(delay);
            }
        }
    }
    

    popup.js

    //popup.js
    function injectTheScript() {
        chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
            // query the active tab, which will be only one tab
            //and inject the script in it
            chrome.tabs.executeScript(tabs[0].id, {file: "content_script.js"});
        });
    }
    document.getElementById('clickactivity').addEventListener('click', injectTheScript);
    

    popup.html:

    <!doctype html>
    <html>
    <head>
        <title>activity</title>
    
        <body>
            <button class="button button0" button id="clickactivity">Update</button>
            <script src="popup.js"></script>
        </body>
    </html> 
    

1 个答案:

答案 0 :(得分:1)

正如您似乎已经确定的那样,问题在于您的内容脚本在单击按钮时如何尝试循环。

您的问题的很大一部分是您正在使用直接循环实现的延迟sleep()。您几乎从不在JavaScript中执行此操作。这样做可以防止其他代码运行。它通常会导致事情中断,CPU使用率最大化。

实现延迟的正确方法是异步的。可以使用setTimeout()获得通用延迟。其中的一点是延迟需要是异步的,以允许其他代码运行,以便它能够完成您想要的任务。如果您想要关注DOM中的更改并收到更改通知,那么您应该使用MutationObserver代替setTimeout()

实施setTimeout()循环

有很多方法可以组织你正在做的事情。下面,我实现了一个setTimeout()循环,如果不满足某些条件,它基本上是一个在延迟后调用自身的函数。

对于这个循环,我们需要的第一件事是一个函数,它将按顺序点击下一个按钮并返回true,直到没有更多按钮需要点击为止,此时它返回{ {1}}:

false

删除重复的代码:迭代按钮类型

正如您所看到的,代码中有相当多的重复。我们可以重新排列它,这样我们只需更改我们正在寻找的按钮类型就可以运行相同的代码。

以下使用Array.prototype.some(),它循环一个数组,直到被调用函数第一次返回function clickNextButton() { //This will click the next button in the sequence which we desire. // A single button is moved from "update" (click) -> "save" (click) -> "saved". // No other buttons are clicked until the first one we find is no longer in the // "update" or "save" states. // It returns false when there are no more buttons to click; //If there is a button in the "save" state, then we need to deal with it first and // not move to the next "update" button until the "save" button changes to "saved". var firstSaveButton = document.querySelector('.saveButton'); if(firstSaveButton) { let myFlagClass = 'myExtensionNameSaveClicked'; if(firstSaveButton.classList.contains(myFlagClass)) { //We have already clicked this button. We need to wait for it to change state. return true; //There may be more to do. } //else firstSaveButton.classList.add(myFlagClass); firstSaveButton.click(); return true; //There may be more to do. } //else //There is no button currently in the "save" state. //Check to see if there is a button in the "update" state. var firstUpdateButton = document.querySelector('.updateButton'); if(firstUpdateButton) { let myFlagClass = 'myExtensionNameUpdateClicked'; if(firstUpdateButton.classList.contains(myFlagClass)) { //We have already clicked this button. We need to wait for it to change state. return true; //There is more to do. } //else firstUpdateButton.classList.add(myFlagClass); firstUpdateButton.click(); return true; //There is more to do. } //else //There are no buttons in the "update" or "save" state. We are done. return false; } 。它是循环数组的好方法,直到满足某些条件,然后不为其余元素调用。在考虑处于true状态的按钮之前,这用于处理"save"状态中的任何按钮。

"update"

我们现在有代码点击下一个按钮,并告诉我们是否没有更多按钮可供点击。所以,我们只需要一个循环来持续调用该函数,直到没有更多按钮可以点击。我们希望每次调用它之间都有延迟。您可以通过几种不同的方式等待DOM中的更改。使用MutationObserer可以让您实际等待DOM中的更改。在这种情况下,假设我们不需要在状态发生变化时立即知道 ,并且我们对变化状态的检查可以合理地降低成本(几个DOM行走),我们可以使用setTimeout()循环检查每隔一段时间。

实际的function clickNextButton() { //This will click the next button in the sequence which we desire. // A single button is moved from "update" (click) -> "save" (click) -> "saved". // No other buttons are clicked until the first one we find is no longer in the // "update" or "save" states. // It returns false when there are no more buttons to click; //If there is a button in the "save" state, then we need to deal with it first and // not move to the next "update" button until the "save" button changes to "saved". return ['save','update'].some(function(type) { var firstButton = document.querySelector('.' + type + 'Button'); if(firstButton) { let myFlagClass = 'myExtensionName' + type + 'Clicked'; if(firstButton.classList.contains(myFlagClass)) { //We've already clicked this button; need to wait for it to change state. return true; //There may be more to do. } //else firstButton.classList.add(myFlagClass); firstButton.click(); return true; //There may be more to do. } //else return false; //No buttons of this type exist. }); } 循环

在下面的代码中,我选择每200ms检查一次按钮的变化。根据您的描述,您需要经常检查。如果确实想要,如果您需要点击个按钮(数百个),那么您可能会减少到100毫秒(即减少时间的地方)检查之间会节省一些大量的时间)。

这可以为此执行setTimeout()循环[注意:为了使基本setTimeout()循环更清晰,这不包含在完成后执行的一些清理代码。该代码已添加到下面的代码段中。]:

setTimeout()

将所有内容与一些代码放在一起以显示发生的事情

以下代码段将所有代码放在一起。它添加了一些代码来清理用作标志的类,以指示单击了一个按钮。此外,还有一些代码可以通过更改按钮的状态来模拟网页正在做什么。

作为清理代码的一部分,spread syntax用于将HTMLCollection返回的getElementsByClassName()转换为数组。然后迭代这些元素以删除我们用作标志的类。这可能会更加优化。它可以利用function clickMoreButtons() { if(clickNextButton()){ //Keep clicking buttons until there are none in the "update" or "save" states. setTimeout(clickMoreButtons,200); //Call this function again in 200ms. } } 永远不应该在没有myExtensionNameupdateClicked的元素上的事实。然而,在这里不进行优化并不会花费那么多,并且如果页面的操作改变直接来自&#34;更新&#34;为了保存&#34;,我们不会错过一些元素。您可以在此处使用querySelectorAll()来获取所有类,但myExtensionNameSaveClickedquerySelectorAll()慢约10倍,因此它实际上不是优化。

HTML块中的CSS,HTML和JavaScript只是用于模拟网页中发生的事情的代码。只应为您的内容脚本考虑JavaScript时钟中的代码。

&#13;
&#13;
getElementsByClassName()
&#13;
function clickNextButton() {
    //This will click the next button in the sequence which we desire.
    //  A single button is moved from "update" (click) -> "save" (click) -> "saved".
    //  No other buttons are clicked until the first one we find is no longer in the
    //    "update" or "save" states.
    //  It returns false when there are no more buttons to click;
    //If there is a button in the "save" state, then we need to deal with it first and
    //  not move to the next "update" button until the "save" button changes to "saved".
    return ['save','update'].some(function(type) {
        var firstButton = document.querySelector('.' + type + 'Button');
        if(firstButton) {
            let myFlagClass = 'myExtensionName' + type + 'Clicked';
            if(firstButton.classList.contains(myFlagClass)) {
                //We've already clicked this button;
                //  need to wait for it to change state.
                return true; //There may be more to do.
            } //else
            firstButton.classList.add(myFlagClass);
            firstButton.click();
            return true; //There may be more to do.
        } //else
        return false; //No buttons of this type exist.
    });
}

function clickMoreButtons() {
    if(clickNextButton()) {
        //Keep clicking buttons until there are none in the "update" or "save" states.
        setTimeout(clickMoreButtons,200); //Call this function again in 200ms.
    } else {
        //Clean up the classes we used as flags.
        let flagClasses=['myExtensionNameupdateClicked','myExtensionNamesaveClicked'];
        //For all flag classes:
        flagClasses.forEach(function(flagClass){
            //Get the list of elements with that class, convert it to an Array, then
            //  iterate through each, removing the class.
            [...document.getElementsByClassName(flagClass)].forEach(function(element) {
                element.classList.remove(flagClass);
            });
         });
        console.log('All done');
    }
}

clickMoreButtons();
&#13;
.updateButton {
    background: orange;
}
.saveButton {
    background: lightblue;
}
.savedButton {
    background: lightgreen;
}
&#13;
&#13;
&#13;