我正在尝试自动化我的页面测试用例,以便用户能够从Chrome扩展程序按下按钮(更新)以及每个按钮匹配我想要的网页上的类,一次一个,循环它们通过:
class="updateButton"
。需要点击一次。点击后,网页会在某个时刻将按钮转换为“保存”状态。class="saveButton"
。需要点击一次。点击后,网页会在某个时刻将按钮转换为“已保存”状态。每个按钮进入“已保存”状态后,我们可以转到下一个按钮。我想将所有匹配的按钮处理到“已保存”状态。
我很亲密。但是,当我得到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>
答案 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()
来获取所有类,但myExtensionNameSaveClicked
比querySelectorAll()
慢约10倍,因此它实际上不是优化。
HTML块中的CSS,HTML和JavaScript只是用于模拟网页中发生的事情的代码。只应为您的内容脚本考虑JavaScript时钟中的代码。
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;