4

How do I iterate through elements with a particular class name on a page and click on them? I tried using

browser.elements('css selector', element.articleItemTitle,function (links) {
        for (var i=0; i < links.value.length; i++) {
            browser.waitForElementVisible('body');
            browser.pause(1000);
            browser.elementIdClick(links.value[i].ELEMENT);
            browser.waitForElementVisible('.article-detail');
            browser.expect.element(element.postHeroTitle).to.be.present;
            browser.back();
        }
});

But it doesn't always click on a specified element each time it iterates through the loop.

I get the error: stale element reference: element is not attached to the page document

using nightwatch

Brown A
  • 929
  • 4
  • 14
  • 21

2 Answers2

4

You can't change the DOM (e.g. by loading a new page) while iterating a list (links) of elements from another page. As soon as you change the page you risk invalidating the list, in which case next time you loop you'll get that error.

Don't think of elements as just being collections of properties, but as being 'proxy' objects into a real page of data in a real browser. They're only good on the current page, and are not to be trifled with.

As for a solution, in your case the only thing you use from the links elements seems to be the Id, which is just a harmless string. So rather than iterating through the matching elements, first build a list of all the Id strings, and iterate through that instead. That should be the only change you need.

Update:

Example code (not tested, and not elegant JavaScript, but likely to work, and demonstrates concept):

var ids = {};

// build the list of matching Ids on current page
browser.elements('css selector', element.articleItemTitle,function (links) {
    ids.push(links.value[i].ELEMENT);
});

// Try each one in turn - note *no references* held to existing page
for (var i = 0; i < ids.length; i++) {
    browser.waitForElementVisible('body');
    browser.pause(1000);
    browser.elementIdClick(ids[i]);
    browser.waitForElementVisible('.article-detail');
    browser.expect.element(element.postHeroTitle).to.be.present;
    browser.back();
}
Community
  • 1
  • 1
Andrew Regan
  • 5,087
  • 6
  • 37
  • 73
  • 3
    that won't work because the callback for elements gets called after the for loop so by the time we call the for loop the ids array is empty – Brown A Mar 21 '16 at 20:25
  • You're probably right - I will ditch my code sample as I don't have time to make it fully runnable, but my aim was to demonstrate that you need to collect up all your Ids first, *then* iterate through them. It's perfectly possible with nested callbacks. – Andrew Regan Mar 21 '16 at 20:31
  • your code is not working at all, not even with for-loop fixing. everything has to be inside the elements function – Panos Vakalopoulos Nov 11 '19 at 15:57
0

I've also come across similar problem and able to resolve it. I've posted sample code here. it's just an example how we can do. Please adapt the same to your situation.

Janardhan Reddy
  • 156
  • 1
  • 8