I'm trying to evaluate the javascript on a page before I do a query because the html that I'm looking for doesn't exist in the AngleSharp document.
There is a method: document.ExecuteScript(string )
But I don't know how to use it compared to how I've seen other libraries used. For example, some python code looks like this...
wait.until(presence_of_element_located((By.ID, "class-name")))
Which just pauses the code I guess until the entire page is evaluated. Elements can then be searched.
In AngleSharp it looks like I have to run ExecuteScript method to do the same thing. But it just throws an exception (Jint.Runtime.JavaScriptException: 'results is not defined') and it returns an object as a result - which is completely obfuscating, not helpful at all.
What do I do so that my next command:
IHtmlCollection<IElement> cells = document.QuerySelectorAll(s);
actually looks through the entire document and not just the initial HTML?
I think there is more than just one question here. So I'll break it down.
First, let's get some basics here:
AngleSharp is a browser core - its not a browser, and it is not a JS engine.
AngleSharp provides the ability to extend it with, for instance, a JS engine. AngleSharp.Js is such an engine based on Jint, however, its still experimental and complicated scripts will definitely not run.
Before we already get into the event loop and async loading details, I'd recommend to make sure that whatever script you expect to run, really runs.
Now to the specifics:
ExecuteScript is a little helper from AngleSharp.Js that actually runs a piece of JS code that you provide. I guess its not at all what you want.
If you just want to "wait" until something is there you can do that with a few lines of C# code:
var maxTime = TimeSpan.FromSeconds(1.5);
var totalTime = TimeSpan.Zero;
var pollTime = TimeSpan.FromMilliseconds(25);
while (totalTime < maxTime)
{
await Task.Delay(pollTime.Milliseconds);
// check condition
if (document.QuerySelector(".foo.bar") != null)
{
// run zoned code
break;
}
totalTime += pollTime;
}
AngleSharp.Js actually has various methods to get into the event loop, i.e., to wait until JS has completed the current work.
For instance, WaitUntilAvailable() can be used to wait until the load event (and related) has been handled.
To enqueue some action the Then() extension method was added. All these extension methods live directly on the IDocument instance.
Related
I've been using selenium (with python bindings and through protractor mostly) for a rather long time and every time I needed to execute a javascript code, I've used execute_script() method. For example, for scrolling the page (python):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
Or, for infinite scrolling inside an another element (protractor):
var div = element(by.css('div.table-scroll'));
var lastRow = element(by.css('table#myid tr:last-of-type'));
browser.executeScript("return arguments[0].offsetTop;", lastRow.getWebElement()).then(function (offset) {
browser.executeScript('arguments[0].scrollTop = arguments[1];', div.getWebElement(), offset).then(function() {
// assertions
});
});
Or, for getting a dictionary of all element attributes (python):
driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', element)
But, WebDriver API also has execute_async_script() which I haven't personally used.
What use cases does it cover? When should I use execute_async_script() instead of the regular execute_script()?
The question is selenium-specific, but language-agnostic.
When should I use execute_async_script() instead of the regular execute_script()?
When it comes to checking conditions on the browser side, all checks you can perform with execute_async_script can be performed with execute_script. Even if what you are checking is asynchronous. I know because once upon a time there was a bug with execute_async_script that made my tests fail if the script returned results too quickly. As far as I can tell, the bug is gone now so I've been using execute_async_script but for months beforehand, I used execute_script for tasks where execute_async_script would have been more natural. For instance, performing a check that requires loading a module with RequireJS to perform the check:
driver.execute_script("""
// Reset in case it's been used already.
window.__selenium_test_check = undefined;
require(["foo"], function (foo) {
window.__selenium_test_check = foo.computeSomething();
});
""")
result = driver.wait(lambda driver:
driver.execute_script("return window.__selenium_test_check;"))
The require call is asynchronous. The problem with this though, besides leaking a variable into the global space, is that it multiplies the network requests. Each execute_script call is a network request. The wait method works by polling: it runs the test until the returned value is true. This means one network request per check that wait performs (in the code above).
When you test locally it is not a big deal. If you have to go through the network because you are having the browsers provisioned by a service like Sauce Labs (which I use, so I'm talking from experience), each network request slows down your test suite. So using execute_async_script not only allows writing a test that looks more natural (call a callback, as we normally do with asynchronous code, rather than leak into the global space) but it also helps the performance of your tests.
result = driver.execute_async_script("""
var done = arguments[0];
require(["foo"], function (foo) {
done(foo.computeSomething());
});
""")
The way I see it now is that if a test is going to hook into asynchronous code on the browser side to get a result, I use execute_async_script. If it is going to do something for which there is no asynchronous method available, I use execute_script.
Here's the reference to the two APIs (well it's Javadoc, but the functions are the same), and here's an excerpt from it that highlights the difference
[executeAsyncScript] Execute an asynchronous piece of JavaScript in
the context of the currently selected frame or window. Unlike
executing synchronous JavaScript, scripts executed with this method
must explicitly signal they are finished by invoking the provided
callback. This callback is always injected into the executed function
as the last argument.
Basically, execSync blocks further actions being performed by the selenium browser, while execAsync does not block and calls on a callback when it's done.
Since you've worked with protractor, I'll use that as example.
Protractor uses executeAsyncScript in both get and waitForAngular
In waitForAngular, protractor needs to wait until angular announces that all events settled. You can't use executeScript because that needs to return a value at the end (although I guess you can implement a busy loop that polls angular constantly until it's done). The way it works is that protractor provides a callback, which Angular calls once all events settled, and that requires executeAsyncScript. Code here
In get, protractor needs to poll the page until the global window.angular is set by Angular. One way to do it is driver.wait(function() {driver.executeScript('return window.angular')}, 5000), but that way protractor would pound at the browser every few ms. Instead, we do this (simplified):
functions.testForAngular = function(attempts, callback) {
var check = function(n) {
if (window.angular) {
callback('good');
} else if (n < 1) {
callback('timedout');
} else {
setTimeout(function() {check(n - 1);}, 1000);
}
};
check(attempts);
};
Again, that requires executeAsyncScript because we don't have a return value immediately. Code here
All in all, use executeAsyncScript when you care about a return value in a calling script, but that return value won't be available immediately. This is especially necessary if you can't poll for the result, but must get the result using a callback or promise (which you must translate to callback yourself).
I am having some trouble with a bit of code. I have a function that does some stuff to some data, calls a remote system (activating a script on that system and passing in the data), and then makes another call to the same system to activate a different script (which acts on the data saved above). The problem is that the 1st call to the remote system appears to get lost in the execution.
This is being run in Safari, uses jquery; the function is tied to a button click, which is defined in the javascript code with an onclick function (i.e. it is not defined in the html button definition).
Here's a rough breakdown of the function (cleaned out for viewing purposes - I hope I left enough to make it clear):
function compareJSON() {
// loop through the objects, testing and changing data
// ...
dataSession=({ //build object for output });
$.each( dataSession.chapters , function( indexC, value ) {
//compare objects to some others, testing and changing data
});
// ...
//Call remote script on other system
urlString="url://blah.dee.com/Blar?script=SaveJSON&$JSONobject=";
window.location= urlString + JSON.stringify(dataSession);
//Call remote script on other system
window.location="url://blah.dee.com/Blar?script=EditJSON";
}
The last three lines of code are the two calls. It uses the window.location to actually trigger the remote system, passing the data through the URL. But I need BOTH scripts to get called and run. It appears that only the LAST script in the sequence ever gets run. If I switch them around it remains whatever is in last place.
Is there something about the window.location that doesn't actually process until the end of the function?
This script actually used to be a series of separate function calls, but I figured I was running into asynchronous execution that was causing the various script calls to not register. But once I put the code into this single function, it was still happening.
Any clues would be helpful.
Thanks,
J
Modifing the value of window.location is reserved exclusively for instances in which you'd like to cause a browser redirect.
It looks like you want to trigger a page request instead. You say you already have jQuery loaded, if so, you can trigger such a request using jQuery.get or a similar function.
For example:
// Loads the myscript.php page in the background
$.get('myscript.php');
// You can also pass data (in the form of an object as the second argument)
$.get('myscript.php', { name: "John", time: "2pm" });
I'm loading a Javascript file that has a function in it that I'd like to call. This is not possible, since between 'initiating' the load, and actually calling the function, the JS isn't loaded yet. Of course, I could do something like setTimeout('functionCall();',5000);, but I don't think this is a very efficient method, and it seems really unstable to me. That's why I was wondering whether there was a better way to do it.
Here's the code I'm using. The function in question is called controllerStart. If I comment out the last line here, and type it into a Javascript terminal (like on Chrome developer tools), everything works.
function loadController(name){
clearAll();
scriptContainer.innerHTML = '';
var scriptElement = document.createElement('script');
scriptElement.setAttribute('src','controllers/' + name + '.js');
scriptElement.setAttribute('type','text/javascript');
scriptContainer.appendChild(scriptElement);
controllerStart();// <-- Doesn't work from here, but works on a terminal
}
Thanks for taking a look!
call the function after you reference the Javascript.
JS loading is SYNCHRONOUS.
So.....
---- js library call -----
----- call the function here -----
Profit!
edit: specifically :
function loadController(name){
clearAll();
scriptContainer.innerHTML = '';
var scriptElement = document.createElement('script');
scriptElement.setAttribute('src','controllers/' + name + '.js');
scriptElement.setAttribute('type','text/javascript');
scriptContainer.appendChild(scriptElement);
scriptElement.onload = new function(){controllerStart();};
//something like that.
}
edit 2: my bad - use "onload" not "load" - too much jquery on the brain
more about "onload" here: http://www.onlinetools.org/articles/unobtrusivejavascript/chapter4.html
There is a good post from Nicholas C. Zackas about it which you can read here, that includes just the code you want to use.
Basically it's just a function that includes both a URL of a JS file to load, and a callback to execute when it's been loaded. It then creates the <script> tag, attaches the callback to execute after it's loaded (via the onreadystatechange or the onload event) and inserts it into the DOM.
Then, all you need to do is call it like:
loadScript('controllers/' + name + '.js', controllerStart);
From the sound of it you're loading your JavaScript asynchronously. In more recent browsers there's an onload event on the tag which fires when it's finished loading. But if you need cross-browser support, the only way to do it is to either:
(a) if it's loaded from the same server as your page, load your javascript using AJAX and then execute it with eval() when it's loaded (instead of using <script> tags),
(b) if you control the file add an event trigger to the end of it which you then listen for in your main JavaScript file. JQuery's bind() and trigger() functions are handy for that.
(c) if it's loaded from somewhere else and you don't control it (but do have permission to redistribute it), relocate it to your server and follow (a).
You also could do away with asynchronous loading and just put a stream of <script> tags in your header, the old fashioned way. That guarantees that each script won't be run until after the previous script has finished.
I have a bizarre need here, and I am unsure of it's feasibility. I can only think how I would do it using threads (To create another thread that performs a Quine function along side a thread running the script I want to Quine and execute at the same time (without manually adding alerts everywhere!!!), but javascript doesn't have that functionality, right?
I have some JavaScript which is interpreted by an application with minimal JavaScript debugging capability of it's own. My script is falling over and since it also uses some statements only understood by this application I cannot debug this within Firefox or Internet Explorer because they cannot handle these custom statements. What I am hoping I'll be able to achieve is a program that can mimic another program but also perform it's own functions.
For arguments sake say I have a script called hello.js that outputs "Hello World!" 100 times and when provided to the application it interprets this, however falls over at some point but I can't tell why, where and when because of the limited debug capability.
function main(){
for(var i=0; i<100; i++){
alert('Hello World!\n');
}
}
I then want to have a script that I can pass to the application instead that will pretend to be the script above but will also alert before each statement is executed. I could just do this:
function main(){
alert('main()')
for(var i=0; i<100; i++){
alert("alert('Hello World!\n');");
alert('Hello World!\n');
}
}
However I am sure you can see how for a long program this would be an arduous task. Do instead I want to make a program that can perform like this:
function main(){
var text = loadJSScript("C:\\Script\\Hello.js"); //Loads a text/javascript file line by line into an array
for(var i=0; i<text.length; i++){
var statement = text[i];
alert("Line Number: " + i + " Statement: " + statement); //Output the line number and statement before executing
execute(statement); //A function that executes the statement from the other file (as if a separate thread)
}
}
This really depends on your target audience (browser use).
You can get a very effective form of pseudo-threading by using web workers.
http://ejohn.org/blog/web-workers/
However this is not widely supported. You could use this where available.
You can also get some behaviour similar to threading by using setTimeout to execute the code.
You can see how timers are queued here:
http://ejohn.org/blog/how-javascript-timers-work/
You could also do the Quine calculation server side, and call using AJAX. This does give an asynchronous request.
These last 2 options aren't threading, but the code will not wait for a response before continuing.
To my knowledge, JavaScript is purely mono-threaded.
Moreover, the loadJSScript("C:\\Script\\Hello.js"); statement is fishy, since you mention browsers. JS in browsers (in Web pages, at least) cannot access the filesystem (beside, that's not portable!).
You can perhaps do something like that in Microsoft's JScript (in WSH), though.
I'm using jQuery to change the HTML of a tag, and the new HTML can be a very long string.
$("#divToChange").html(newHTML);
I then want to select elements created in the new HTML, but if I put the code immediately following the above line it seems to create a race condition with a long string where the changes that html() is making may not necessarily be finished rendering. In that case, trying to select the new elements won't always work.
What I want to know is, is there an event fired or some other way of being notified when changes to html() have finished rendering ? I came across the jQuery watch plugin, which works alright as workaround but it's not ideal. Is there a better way ?
As a commenter already mentioned, JavaScript is single threaded, so you can't get race conditions.
What may trip you up however, is the fact that the UI will not update itself based on JavaScript, until a thread is finished. This means that the entire method must finish, including all code after you call html(...), before the browser will render the content.
If your code after calling html(...) relies on the layout of the page being recalculated before continuing, you can do something like this:
$("#divToChange").html(newHTML);
setTimeout(function() {
// Insert code to be executed AFTER
// the page renders the markup
// added using html(...) here
}, 1);
Using setTimeout(...) with a time of 1 in JavaScript defers execution until after the current JavaScript code in the calling function finishes and the browser has updated the UI. This may solve your problem, though it is difficult to tell unless you can provide a reproducible example of the error you're getting.
use .ready jQuery function
$("#divToChange").html(newHTML).ready(function () {
// run when page is rendered
});
It's 7 years latter and I just ran into a scenario exactly like the one #mikel described, where I couldn't avoid a "timer based solution". So, I'm just sharing the solution I developed, in case anyone out there is still having issues with this.
I hate having setTimeouts and setIntervals in my code. So, I created a small plugin that you can put where you think it's best. I used setInterval, but you can change it to setTimeout or another solution you have in mind. The idea is simply to create a promise and keep checking for the element. We resolve the promise once it is ready.
// jquery.ensure.js
$.ensure = function (selector) {
var promise = $.Deferred();
var interval = setInterval(function () {
if ($(selector)[0]) {
clearInterval(interval);
promise.resolve();
}
}, 1);
return promise;
};
// my-app.js
function runWhenMyElementExists () {
// run the code that depends on #my-element
}
$.ensure('#my-element')
.then(runWhenMyElementExists);