I saw this code: https://gist.github.com/sidneys/ee7a6b80315148ad1fb6847e72a22313
This is pretty nice, a way to detect changes inside a page. I have the following code in addition to the above one:
(async () => {
const elems = await Promise.all([
'elem1',
'elem2',
'elem3',
].map(e => onElementReady(e, true)));
console.log(elems);
})();
The reason why I need to check for multiple elements is that there's a chaotic order of elements loading up via AJAX and one element isn't enough because it might be too early when the page is not fully loaded.
But this code that was provided by #wOxxOm doesn't work well when I need to call it every time a new page has loaded through AJAX. It works the first time when run, afterwards calling again the function that includes the above-mentioned code will no longer work.
Can you please help me?
Related
I searched everywhere a simple example for loop a event whiteout the processor up to 70%. I don't find the awsner so i need help. I just need to everytime the page refresh it do the code.
Here the code:
page.once('load', async () => {
console.log("Page loaded!")
// Example of code to execute when the page reload
const searchBtn = await login.$x("//button[#id='btnEnter']");
await searchBtn[0].click();
});
You need code which executes everytime the page was loaded. You can achieve that by calling
function onLoadHandler() {
/**code goes here ***/
}
page.on('load', onLoadHandler)
You do not have to loop over this. Your onLoadHandler function will be executed everytime this particular page is reloaded.
What you did is to call page.once('load', onLoadHandler). This listens to the 'load' event once and then the handler removes it selve, it does it "once".
Loop
So probably if you looped the function page.once()(doing it 10000 times a sec) using a while loop or something your processor would ofc reach 70% usage .. Whenever your processor goes crazy like this, your code is suspected to be way to ressource heavy. In general that should not happen when running normal app without handling 10k requests per second or something.
Feel free to leave a comment.
I need to check whether all elements of a class are not present in the DOM. Say, I want all the elements with the class .loading to not present in the DOM. I know I can do this:
browser.wait(EC.stalenessOf($$('.loading')), 5000);
My question is whether this code will wait for all the loading class to go away or just the first one? If it waits for only the first one, how will I make it work for all of them? Thanks in advance :)
yes, this should wait until ALL elements matching the locator are not present
But for future, when in doubt, you can write your function instead of using ExtectedConditions library. In this case, you could do
let loading = $$('.loading');
await browser.wait(
async () => (await loading.count()) === 0,
5000,
`message on failure`
);
In fact, this is what I'm using to handle multiple loading animations ;-)
I have the follwing javascript code that it triggers an IronPython script when I load the report.
The only issue I have is that for a reason I don't know it does it (it triggers the script) a couple of times.
Can some one help me? below is the script:
var n=0;
$(function () {
function executeScript() {
if (n==0){
n=n+1;
now = new Date();
if (now.getTime()-$('#hiddenBtn input').val()>10000){
$('#hiddenBtn input').val(now.getTime());
$('#hiddenBtn input').focus();
$('#hiddenBtn input').blur();
}
}
}
$(document).ready(function(){executeScript()});
strong text});
Please, let me know if you need more information.
Thanks in advance!!!
I have had similar issues with Javascript executing multiple times. Spotfire seems to instance the JS more than once, and it can cause some interesting behavior...
the best solution, in my opinion, only works if users are accessing the document via a link (as opposed to browsing the library). pass a configuration block to set a document property with a current timestamp, which would execute your IP script. this is the most solid implementation.
otherwise, you can try something like this:
// get a reference to a container on the page with an ID "hidden"
var $hidden = $("#hiddenBtn input");
// only continue if the container is empty
if !($hidden.text()) {
var now = Date.now();
$hidden.text(now)
.focus()
.blur();
|}
this is essentially the same as the code you posted, but instead of relying on the var n, you're counting on the input #hiddenBtn > input being empty. there is a caveat that you'll have to ensure this field is empty before you save the document
one addtional solution is using a Data Function to update the document property, like #user1247722 shows in his answer here: https://stackoverflow.com/a/40712635/4419423
I'm prerendering my HTML pages for the search engines bots via PhantomJS through Selenium, so that they can see the fully loaded content. Currently, after PhantomJS reached the page, I'm waiting 5 seconds so that I'm sure everything is loaded.
Instead of waiting those 5 seconds every time, one solution I contemplate is to wait until an attribute html-ready on the <body /> tag is set to true:
<html ng-app>
<head>...</head>
<body html-ready="{{htmlReady}}">
...
</body>
</html>
.controller("AnyController", function($scope, $rootScope, AnyService) {
$rootScope.htmlReady = false;
AnyService.anyLongAction(function(anyData) {
$scope.anyData = anyData;
$rootScope.htmlReady = true;
});
})
The question is: will the html-ready attribute always be set to true after any view update has been done (e.g. displaying the anyData)? In other words, is it possible that during a laps, the html-ready attribute is true while the page is not fully loaded yet? If yes, how can it be handled?
It should be done after the digest, thus it has more chances to work as expected.
AnyService.anyLongAction(function(anyData) {
$scope.anyData = anyData;
$timeout(function () {
$rootScope.htmlReady = true;
}, 0, false);
});
But it is useless in terms of the app. You have to watch for changes in every single place, Angular doesn't offer anything to make the task easier.
Fortunately, you are free to abstract from Angular and keep it simple.
var ignoredElements = [];
ignoredElements = ignoredElements.concat($('.continuously-updating-widget').toArray());
var delay = 200; // add to taste
var timeout;
var ready = function () {
$('body').off('DOMSubtreeModified');
clearTimeout(timeoutLimit);
alert('ready');
};
$('body').on('DOMSubtreeModified', function (e) {
if (ignoredElements.indexOf(e.target) < 0) {
clearTimeout(timeout);
timeout = setTimeout(ready, delay);
}
});
var timeoutLimit = setTimeout(ready, 5000);
Feel free to angularify it if needed, though it isn't the production code anyway.
It is a good idea to put the handler into throttle wrapper function (the event will spam all the way). If you use remote requests on the page that can potentially exceed timeout delay, it may be better to combine this approach with several promises from async services and resolve them with $q.all. Still, much better than looking after every single directive and service.
DOMSubtreeModified is considered to be obsolete (it never was really acknowledged, MutationObserver is recommended instead), but current versions of FF and Chrome support it, and it should be ok for Selenium.
Short answer
No. It isn't guaranteed that your markup will be completely rendered when html-ready is set.
Long answer
To the best of my knowledge it's not possible to accurately determine when Angular has finished updating the DOM after the model changed. In general it happens very fast and it doesn't take more than a few cycles to finish, but that's not always the case.
Correctly detecting when a page has finished loading/rendering is actually quite a challenge, and if you take a look at the source code of specialized tools, like prerender, you'll see that they use several different checks in order to try to decide whether a page is ready or not. And even so it doesn't work 100% of the time (Phantom may crash, a request may take longer than usual to complete, and so on).
If you really want to come up with your own solution for this problem, I suggest that you take a look at prerender's source code (or another similar project) to get some inspiration.
To see the problem in action, see this jsbin. Clicking on the button triggers the buttonHandler(), which looks like this:
function buttonHandler() {
var elm = document.getElementById("progress");
elm.innerHTML = "thinking";
longPrimeCalc();
}
You would expect that this code changes the text of the div to "thinking", and then runs longPrimeCalc(), an arithmetic function that takes a few seconds to complete. However, this is not what happens. Instead, "longPrimeCalc" completes first, and then the text is updated to "thinking" after it's done running, as if the order of the two lines of code were reversed.
It appears that the browser does not run "innerHTML" code synchronously, but instead creates a new thread for it that executes at its own leisure.
My questions:
What is happening under the hood that is leading to this behavior?
How can I get the browser to behave the way I would expect, that is, force it to update the "innerHTML" before it executes "longPrimeCalc()"?
I tested this in the latest version of chrome.
Your surmise is incorrect. The .innerHTML update does complete synchronously (and the browser most definitely does not create a new thread). The browser simply does not bother to update the window until your code is finished. If you were to interrogate the DOM in some way that required the view to be updated, then the browser would have no choice.
For example, right after you set the innerHTML, add this line:
var sz = elm.clientHeight; // whoops that's not it; hold on ...
edit — I might figure out a way to trick the browser, or it might be impossible; it's certainly true that launching your long computation in a separate event loop will make it work:
setTimeout(longPrimeCalc, 10); // not 0, at least not with Firefox!
A good lesson here is that browsers try hard not to do pointless re-flows of the page layout. If your code had gone off on a prime number vacation and then come back and updated the innerHTML again, the browser would have saved some pointless work. Even if it's not painting an updated layout, browsers still have to figure out what's happened to the DOM in order to provide consistent answers when things like element sizes and positions are interrogated.
I think the way it works is that the currently running code completes first, then all the page updates are done. In this case, calling longPrimeCalc causes more code to be executed, and only when it is done does the page update change.
To fix this you have to have the currently running code terminate, then start the calculation in another context. You can do that with setTimeout. I'm not sure if there's any other way besides that.
Here is a jsfiddle showing the behavior. You don't have to pass a callback to longPrimeCalc, you just have to create another function which does what you want with the return value. Essentially you want to defer the calculation to another "thread" of execution. Writing the code this way makes it obvious what you're doing (Updated again to make it potentially nicer):
function defer(f, callback) {
var proc = function() {
result = f();
if (callback) {
callback(result);
}
}
setTimeout(proc, 50);
}
function buttonHandler() {
var elm = document.getElementById("progress");
elm.innerHTML = "thinking...";
defer(longPrimeCalc, function (isPrime) {
if (isPrime) {
elm.innerHTML = "It was a prime!";
}
else {
elm.innerHTML = "It was not a prime =(";
}
});
}