10101
10101

Reputation: 2402

Iterating through website table (Element is not attached to the page document)

I have a code that iterates through website table. Table is dynamic and items are loaded into it on user scroll.

I have parameters:

public class UserTableRow
{
    private readonly IWebElement row;

    public string Username => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l0 r0')]")).Text;
    public string Firstname => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l1 r1')]")).Text;
    public string Lastname => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l2 r2')]")).Text;
    public string Type => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l3 r3')]")).Text;
    public string Crew => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l4 r4')]")).Text;
    public string JobTitle => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l5 r5')]")).Text;
    public string DefaultPrice => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l6 r6')]")).Text;
    public string Future => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l7 r7')]")).Text;
    public string Language => row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l8 r8')]")).Text;

    public override string ToString()
    {
        return "SharePrice: " + Username.ToString() + ": " + Firstname.ToString();
    }

    public UserTableRow(IWebElement row)
    {
        try
        {
            this.row = row;
        }
        catch (Exception)
        {

            throw;
        }
    }
}

Here is the method itself for adding data into List.

    public static IEnumerable<UserTableRow> AddItemsToList(IWebDriver driver)
    {

        IReadOnlyCollection<IWebElement> rows = new List<IWebElement>();

        List<UserTableRow> DataHere = new List<UserTableRow>();

        driver.FindElement(By.XPath("//*[@id=\"disabled_users_show_label\"]")).Click();

        Thread.Sleep(7000);

        driver.FindElement(By.XPath("//*[@id=\"users_table\"]/div[5]/div/div[1]")).Click();

        for (int i = 0; i < 11; i++)
        {
            rows = driver.FindElements(By.CssSelector("#users_table .slick-viewport .slick-row"));

            // Now we will iterate through cells in table and compare to what we already have in the list
            foreach (IWebElement cell in rows)
            {
                rows = driver.FindElements(By.CssSelector("#users_table .slick-viewport .slick-row"));

                // Add data to our object
                DataHere.Add(new UserTableRow(cell));

                //We need to select table first to be able to scroll down. We do it directly here
                Actions actions = new Actions(driver);
                actions.SendKeys(Keys.ArrowDown).Build().Perform();

            }

        }

        List<UserTableRow> noDupes = DataHere.Distinct().ToList();

        return noDupes;
    }

It goes fine but at some point (I don't know why) it catches an error saying:

OpenQA.Selenium.StaleElementReferenceException: 'stale element reference: element is not attached to the page document (Session info: chrome=84.0.4147.105)'

Pointing at row.FindElement(By.XPath(".//div[contains(@class, 'slick-cell l0 r0')]")).Text;

Any ideas how to add try catch to Parameters so code will continue execution until adding to list has been finished?


The reason I am calling

rows = driver.FindElements(By.CssSelector("#users_table .slick-viewport .slick-row"));

Inside the loop is I am trying to update table values to iterate through after scrolling down. This is a tricky table that unloads items and loads other items from database on the scroll. So I am trying to mimic user scroll with keyboard and catch table items to List.

It would be perfect to compare to list items on the flight. However I was thinking to get all to the List (even duplicates) and then later make other list where I will eliminate duplicates wit something like:

    List<UserTableRow> noDupes = DataHere.Distinct().ToList();

HTML:

<div id="users_table" class="security_slick_container slickgrid_300610 ui-widget" style="overflow: hidden; outline: 0px; position: relative;">
    <div tabindex="0" hidefocus="" style="position:fixed;width:0;height:0;top:0;left:0;outline:0;"></div>
    <div class="slick-header ui-state-default" style="overflow:hidden;position:relative;">
        <div class="slick-header-columns" style="left: -1000px; width: 2132px;" unselectable="on">
            <div class="ui-state-default slick-header-column slick-header-sortable slick-header-column-sorted" id="slickgrid_300610userName" title="" style="width: 94px;"><span class="slick-column-name"><strong>Username:</strong></span><span class="slick-sort-indicator slick-sort-indicator-asc"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610firstName" title="" style="width: 89px;"><span class="slick-column-name"><strong>Firstname:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610lastName" title="" style="width: 109px;"><span class="slick-column-name"><strong>Lastname:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610type" title="" style="width: 124px;"><span class="slick-column-name"><strong>Type:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610crew" title="" style="width: 109px;"><span class="slick-column-name"><strong>Crew:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610jobTitle" title="" style="width: 109px;"><span class="slick-column-name"><strong>Job title:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column slick-header-sortable" id="slickgrid_300610defaultPriceClass" title="" style="width: 124px;"><span class="slick-column-name"><strong>Defaultprice class:</strong></span><span class="slick-sort-indicator"></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column" id="slickgrid_300610description" title="" style="width: 129px;"><span class="slick-column-name"><strong>Description:</strong></span>
                <div class="slick-resizable-handle"></div>
            </div>
            <div class="ui-state-default slick-header-column" id="slickgrid_300610language" title="" style="width: 39px;"><span class="slick-column-name"><strong>Language:</strong></span>
                <div class="slick-resizable-handle"></div>
            </div>
        </div>
    </div>
    <div class="slick-headerrow ui-state-default" style="overflow: hidden; position: relative; display: none;">
        <div class="slick-headerrow-columns" style="width: 1115px;"></div>
        <div style="display: block; height: 1px; position: absolute; top: 0px; left: 0px; width: 1132px;"></div>
    </div>
    <div class="slick-top-panel-scroller ui-state-default" style="overflow: hidden; position: relative; display: none;">
        <div class="slick-top-panel" style="width:10000px"></div>
    </div>
    <div class="slick-viewport" style="width: 100%; overflow: auto; outline: 0px; position: relative; height: 567px;">
        <div class="grid-canvas" style="height: 16550px; width: 1115px;">
            <div class="ui-widget-content slick-row even" style="top:0px">
                <div class="slick-cell l0 r0">john.smith</div>
                <div class="slick-cell l1 r1">John</div>
                <div class="slick-cell l2 r2">Smith</div>
                <div class="slick-cell l3 r3">Contractor</div>
                <div class="slick-cell l4 r4">Microsoft</div>
                <div class="slick-cell l5 r5">Sales manager</div>
                <div class="slick-cell l6 r6">A</div>
                <div class="slick-cell l7 r7"></div>
                <div class="slick-cell l8 r8">en</div>
            </div>
            <div class="ui-widget-content slick-row odd" style="top:25px">
                <div class="slick-cell l0 r0">robert.geits</div>
                <div class="slick-cell l1 r1">Robert</div>
                <div class="slick-cell l2 r2">Geits</div>
                <div class="slick-cell l3 r3">Staff</div>
                <div class="slick-cell l4 r4">Google</div>
                <div class="slick-cell l5 r5">Project manager</div>
                <div class="slick-cell l6 r6">B</div>
                <div class="slick-cell l7 r7"></div>
                <div class="slick-cell l8 r8">de</div>
            </div>
            <div class="ui-widget-content slick-row even" style="top:50px">
                <div class="slick-cell l0 r0">amir.rooney</div>
                <div class="slick-cell l1 r1">Amir</div>
                <div class="slick-cell l2 r2">Rooney</div>
                <div class="slick-cell l3 r3">Staff</div>
                <div class="slick-cell l4 r4">Microsoft</div>
                <div class="slick-cell l5 r5">Sales manager</div>
                <div class="slick-cell l6 r6">A</div>
                <div class="slick-cell l7 r7"></div>
                <div class="slick-cell l8 r8">en</div>
            </div>
            # ETC # ETC # ETC # ETC # ETC # ETC # ETC # ETC # ETC # ETC 
        </div>
    </div>
    <div tabindex="0" hidefocus="" style="position:fixed;width:0;height:0;top:0;left:0;outline:0;"></div>
</div>

Upvotes: 0

Views: 118

Answers (1)

Paperclip Bob
Paperclip Bob

Reputation: 386

You are refreshing rows inside the foreach, but rows is the iterable object of the foreach. That I guess isn't illegal (after all, it compiled) but seems asking for trouble. My opinion is the stale element reference is somehow related to rows being reassigned within its own foreach loop.

However, after you assign rows the 2nd time, you never use it inside the foreach. It almost seems like it is an unintentional statement that you can delete. After all, why bother assigning rows inside the foreach and then never use it inside the foreach?

If you are assigning rows a 2nd time because you are comparing to what's already been processed, I would give the 2nd rows assignment a different variable name (say, rows_B). But as I said, I don't see it happening here. You just assign rows inside foreach and never use it.

Or, if the reason for the 2nd rows assignment because the table is dynamically changing while you're processing the table? If so, I think the best you can do is catch the error and try to recover from whatever changed on you midstream. I know that sounds vague, but really, if your input data is changing while you're trying to process that input the best you could ever hope for is a best-effort effort.

For example, you could create a 2nd rows variable (say, rows_b) to refresh within the foreach loop. Then compare rows_b with the original rows. If they are identical - awesome, just keep processing.

If they differ, I would keep track of which rows you've processed to that point, break out of the loop and start over. During the foreach you'd check if a row had been previously processed and if so skip to the next iteraton.

If this is what you're trying to do - and want to see a code example - let me know. I didn't want to write the code if it isn't even what you're trying to accomplish ;-)

Upvotes: 1

Related Questions