Protractor循环中无法点击元素及元素定位问题求助
Hey there! Let's walk through what's going wrong with your Protractor code and get those product clicks working properly. First, let's break down the issues in each of your attempted solutions, then share the correct implementations.
What's Wrong With Your Current Approaches?
Let's go through each failed attempt one by one:
Scheme A & B
The core mistake here is using products.getText() inside the filter callback instead of elem.getText().
productsis anelement.all()collection, soproducts.getText()returns an array of all product texts, not the text of the individual element being filtered.- This means your condition
text === 'product4'will never be true, so the filter returns an empty array. That's why Scheme A does nothing, and Scheme B throws an "index out of bound" error (you're trying to click the first element of an empty set).
Scheme C
This approach has multiple issues:
- The filter callback doesn't return a boolean Promise (which is required for
filter()to work). Instead, you're running anexpect()and a click inside the callback, which breaks the filtering logic entirely. - You're still referencing
productsinstead of the individualelemin the callback, and your XPath locator doesn't relate to the product name element you're targeting.
Scheme E
- The
map()method returns an array of all product texts, sotxt == 'product4'will never be true (you're comparing an array to a string). - Additionally, the
itemvariable is out of scope in thethen()callback—you can't access it there, so even if the condition worked,item.click()would throw an error.
Scheme F
When you click a product and navigate back, the original DOM elements you stored in products become stale (they're no longer attached to the page document). Reusing these stale references causes the error you're seeing.
Correct Solutions
1. Filter for a Specific Product & Click It
To properly filter and click the "product4" element, use the individual elem in the filter callback:
var products = element.all(by.className('productNameHover')); products.filter(function(elem) { // Get text of the CURRENT element being filtered, not the entire collection return elem.getText().then(function(text) { return text === 'product4'; }); }).first().click().then(function() { // Optional: Wait after click (better to use ExpectedConditions instead of sleep) browser.sleep(5000); });
2. Fixing the Loop & Stale Element Issue
If you need to click every product and navigate back, avoid reusing stale element references by re-locating the element each time:
var products = element.all(by.className('productNameHover')); // First get all product names products.map(function(item) { return item.getText(); }).then(function(productNames) { productNames.forEach(function(name) { // Re-locate the product element by its text every time element(by.cssContainingText('.productNameHover', name)).click(); browser.sleep(5000); // Re-locate the back button too, to avoid stale references var backButton = element.all(by.className('btn btn-outline btn-big mt-3 ml-0 ml-sm-2')).first(); backButton.click(); browser.sleep(5000); }); });
3. Better Practice: Use ExpectedConditions Instead of Sleep
Instead of relying on fixed browser.sleep() calls (which are unreliable), use Protractor's ExpectedConditions to wait for elements to be ready:
var EC = protractor.ExpectedConditions; var products = element.all(by.className('productNameHover')); products.filter(function(elem) { return elem.getText().then(text => text === 'product4'); }).first().click().then(function() { var backButton = element.all(by.className('btn btn-outline btn-big mt-3 ml-0 ml-sm-2')).first(); // Wait until the back button is clickable before interacting browser.wait(EC.elementToBeClickable(backButton), 10000); backButton.click(); });
内容的提问来源于stack exchange,提问作者kavitha

