Forum Discussion

azakharov's avatar
azakharov
Contributor
12 years ago

Wait for element

Hi all,



Is there a way to reavaluate xpath or css element by using an existant variable already asigned to it? 



For example why this one doesn't work (JScript):


var element = page.FindChildByXPath("//div[text()='A 08012012 template']"); // element is not loaded on the page yet. It needs few seconds to appear on the page


aqUtils.Delay(10000); //make a pause

element; // call for variable with xpath again


element.Click(); // returns error even if element is loaded


 

But this works:


var element = page.FindChildByXPath("//div[text()='A 08012012 template']"); 


aqUtils.Delay(10000);

var element = page.FindChildByXPath("//div[text()='A 08012012 template']"); // declare element again


element.Click(); // now it works



Why do I have to declare element again even if I have already declared it earlier?

Is there a method that does that, something like element.TryAgain()?



I want to write a reliable function that takes any css/xpath object and only allows script to proceed when it is loaded. This way I will only need to write it once and reuse it evertime I need it later.



Thanks.

  • AlexKaras's avatar
    AlexKaras
    Icon for Champion Level 3 rankChampion Level 3
    Hi Andrey,



    The case is that

    var element = page.FindChildByXPath("//div[text()='A 08012012 template']");

    line of code contains a lot of actions executed in sequence.

    First, it declares an 'element' variable. This, in short, allocates a space for the pointer in the stack and initializes this pointer with initial empty value;

    Second, the call to page.FindChildByXPath() function is made. Depending on the result, the function returns either (pointer to the) object or some scalar value. On exit from the function, the runtime system allocates space in the heap and copies returned object or value to the created data structure;

    Third, the system updates the value of the element variable in the stack so that it points to the created structure in the heap.



    With the above in the mind, it is obvious that something like <object>.TryAgain() is possible only if the object provides such a method, i.e. only if it 'knows' how to re-evaluate itself.



    It seems to me that .FindChildByXPath() is just a wrapper around the native function provided by web browser and is not designed to provide returned object with the method to re-evaluate itself.



    In order to create reusable function, you may consider a loop that executes no more than certain number of attempts or seconds. Within the loop you need to execute the .FindChildByXPath() function and check the returned value. Exit from the loop if the function returned valid object (i.e. if object.Exists is true). Delay for some short period of time (e.g. 500ms) and execute the loop one more time if the function returned non-existing object or null.



    Hope, the above will help.
  • AlexKaras's avatar
    AlexKaras
    Icon for Champion Level 3 rankChampion Level 3
    Welcome, Andrey.



    'If logic' might well be applicable in your case.



    As a simple sample - a VBScript example of the loop I mentioned in my post: 



    iTimeout = Project.Variables.pvtPageTimeout ' e.g. 30000 to wait no more than 30sec

    iStopTime = Win32API.GetTickCount() + iTimeout


    ' Wait for the element to appear

    strMsg = aqString.Format("Waiting for the '%s' element to load...", strValue)

    Do

      Call aqUtils.Delay(500, strMsg)

      ' Call PageCheckForException()

      Set obj = FindChildNodeXPath(ObjectOnPage, strElement, strAttribute, strAttrValue, strValue)

    Loop Until obj.Exists Or (Win32API.GetTickCount() > iStopTime)


  • Thank you Alexei.



    I use both css and xpath locators so I will probably have to create 2 waitfor methods, 1 for css, 1 for xpath.

    Or maybe I can use if logic (if starts with "//" use FindChildByXPath, else use QuerySelector).



    In any event your response is very helpful. Thanks again!



    Andrey.