WebBrowser.Navigate with JavaScript - javascript

I am using the WebBrowser Component in System.Windows.Forms. The code loads content from a website and returns it properly. There is a JavaScript which is executed and loading some of the DOMs after the page has loaded completely.
The JavaScript is not finished loading by the time the .Navigate method finished execution. If I set a Breakpoint on the .Navigate in Debug mode, It will, clearly because .Navigate is asynchronous, run through the process of loading the page including the scripts.
void LoadPageWithScripts() {
Browser.Navigate("mypagewithscriptsurl");
// whatever comes next prevents the DOM generated by the script from beeing loaded
// ... e.g.:
Console.WriteLine("whatever");
// use Browser.Document later
}
I know, this question is similar to the one provided here: JavaScript only works...
Unfortunately, I have no Influence on the page which is loaded, so the approaches I have seen there, are not suitable for my needs.
I have tried to simply work with Thread.Sleep, as suggested by many forums. But even this won't work. As soon as the code continues to run past the .Navigate method, the JavaScript is lost. Only setting a break point on it will work currently.
Browser.Navigate("pageUrl");
Browser.Navigate("pageurl");
// Very bad solution
Thread.Sleep(2000);
while (true)
{
if (Browser.ReadyState == WebBrowserReadyState.Complete)
{
// do something
break;
}
else
{
Application.DoEvents();
}
}
Using the DocumentCompleted Event will not work, since the Script is not loaded before the document is in completed state.
Browser.Navigate("pageUrl");
Browser.DocumentCompleted += (o, e) =>
{
var text = Browser.DocumentText;
Console.WriteLine(text);
};
Hope to find some help.

Related

Qt function runJavaScript() does not execute JavaScript code

I am trying to implement the displaying of a web page in Qt. I chose to use the Qt WebEngine to achieve my task. Here's what I did :
Wrote a sample web page consisting of a empty form.
Wrote a JS file with just an API to create a radio button inside the form.
In my code, it looks like this :
View = new QWebEngineView(this);
// read the js file using qfile
file.open("path to jsFile");
myJsApi = file.Readall();
View->page()->runjavascript (myjsapi);
View->page()->runjavascript ("createRadioButton(\"button1\");");
I find that the runJavaScript() function has no effect on the web page. I can see the web page in the output window, but the radio button I expected is not present. What am I doing wrong?
I think you will have to connect the signal loadFinished(bool) of your page() to a slot, then execute runJavaScript() in this slot.
void yourClass::mainFunction()
{
View = new QWebEngineView(this);
connect( View->page(), SIGNAL(loadFinished(bool)), this, SLOT(slotForRunJS(bool)));
}
void yourClass::slotForRunJS(bool ok)
{
// read the js file using qfile
file.open("path to jsFile");
myJsApi = file.Readall();
View->page()->runJavaScript(myjsapi);
View->page()->runJavaScript("createRadioButton(\"button1\");");
}
I had this problem, runJavascript didn't have any effect. I had to put some html content into the view (with page().setHtml("") before running it.
Check the application output, it might contain JavaScript errors. Even if your JS code is valid, you might encounter the situation where the script is run before DOMContentLoaded event, that is document.readyState == 'loading'. Therefore, the DOM might not be available yet, as well as variables or functions provided by other scripts. If you depend on them for your code to run, when you detect this readyState, either wait for the event or try calling the function later, after a timeout. The second approach with timeout might be needed if you need to get the result of the code execution, as this can be done only synchronously.

best way to load scripts dynamically

I'm working on a project where I've had to greatly widgetize my code. As such, I'm loading 5 pages of JS dynamically into the DOM. The problem I'm having though is that I don't have a way to check and wait for these scripts to be loaded. If it takes them a second to get pushed up into the DOM, then my script tries to run, but fails when it calls on them.
Here's an example of how I'm embedding my scripts.
var script_tag2 = document.createElement('script');
script_tag2.setAttribute("type","text/javascript");
script_tag2.setAttribute("src","http://www.freeptools.com/mapster/js/three.min.js");
(document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag2);
This inserts it into the DOM, but I dont' have a way to pause my code until it's done. Has anyone run into this before? I'm trying to come up with a best practices for this, but coming up short.
Use jQuery.getScript
jQuery.getScript("http://www.freeptools.com/mapster/js/three.min.js")
.done(function() {
/* yay, all good, do something */
})
.fail(function() {
/* boo, fall back to something else */
});

How to include javascript file only once

I want to give clients an HTML block they can include in their site, and this HTML will contain some table and image, plus a javascript that will make manipulations over the HTML block.
so I give them the HTML :
<a data-theme="1" data-srv="http://localhost:50987/" class="block1" href="http://myserver/payment/Details">outer information</a><script type="text/javascript" src="http://myserver/Scripts/checkout.js"></script>
in checkout.js I have included JQuery if no Jquery exists in document and do manipulation over the element $('a.block1') ... the problem is when someone puts this block of HTML more then once over the same page, I want that the client will not call "checkout.js" more then once,
I've tried declaring global var inside "checkout.js" and check if it's exists, it works good to stop doing the same manipulation more then once but I want to stop the call to JS al together .
Javascript runs after it loads, you can't stop the JS running, if it is referenced multiple times. It won't be loaded multiple times, so the overhead of it running again is basically nil.
To stop the behavior of the javascript happening again, just put the check at the top level of the file, put the rest of the file in the conditional, and write to a global variable to make sure you don't run again.
if (window._your_unique_id === undefined) {
window._your_unique_id = true;
// the rest of your javascript
}
that will mean nothing in the script runs. You can still define whatever you like in that if statement, though if you define functions and variables in there, you may have to explicitly put them on the window object, because they'll otherwise be local (but then, it is bad practice to have anything implicitly defined global anyway, so it shouldn't make any difference if your code is well structured).
Just deploy your code as a module.
Ie.
(function(window){
if(window.CheckoutModule) return;
// now you're certain there's no global module loaded
var CheckoutModule = window.CheckoutModule = {};
// you can, ie, add a jQuery check here.
if('undefined' != typeof jQuery) {
// do your jQuery thing here.
}
return ;
})(window, jQuery);

requirejs error on page navigation

I've had this happen in Chrome and IE...
If I click a link to navigate away from a page while requirejs is still loading or getting scripts, I get random errors. Sorry if this is vague...
For example, in require.js itself, I received an error today:
Unable to get value of the property 'normalize': object is null or undefined
In the following block:
//If current map is not normalized, wait for that
//normalized name to load instead of continuing.
if (this.map.unnormalized) {
//Normalize the ID if the plugin allows it.
if (plugin.normalize) {
name = plugin.normalize(name, function (name) {
return normalize(name, parentName, true);
}) || '';
}
//prefix and name should already be normalized, no need
//for applying map config again either.
normalizedMap = makeModuleMap(map.prefix + '!' + name,
I've received other errors in my own defined js files where they start executing the code before the dependencies are fully loaded. I get the feeling that when the browser is asked to navigate away from the current page, it stops all ajax calls and thus truncating some of the js.
Is there a way to prevent these sort of errors?
Avoid running code in the define function's scope. Start your app's logic once everything has loaded by writing your code in functions that get executed when your page is ready.
Also what do you mean by navigate? If you navigate away the page goes away, as well as its errors. Is this pushAPI style navigation with the app staying loaded, or a complete and full browser navigation?

How to only wait for document.readyState in IE and fire instantly for all other browsers?

I have written a CSS and Javascript lazyloader to dynamically load resources for seperate pagelets (in the way that Facebook renders a page with it's BigPipe technology).
In short an HTML frame is rendered first, then separate parts of the page are all generated asynchronously by the server. When each pagelet arrives the pagelets css is loaded first, then its innerHTML is set, then finally we load any required javascript for this pagelet and initialise it.
Everything works perfectly and perceived load time is pretty much instantaneous for any given page.
However in IE, I occasional I get Method does not support method or property when initialising the scripts.
I have solved this by checking for document.readyState before loading the scripts.
Now this isn't a huge issue but it adds on average 170ms to a pageload in chrome or firefox. Which is not needed.
function loadScripts(init){
// ensure document readystate is complete before loading scripts
if( doc.readyState !== 'complete'){
setTimeout(function(){
loadScripts(init);
}, 1 );
}
else{
complete++;
if(complete == instance.length){
var scripts = checkJS(javascript);
if(scripts.length) {
LazyLoad.js(scripts, function(){
runPageletScript();
for (var i = 0; i < scripts.length; ++i) {
TC.loadedJS.push(scripts[i]);
}
});
}
else{
runPageletScript();
}
}
}
}
What I am looking for is a modification to this script which will only implement the 'wait' in IE, if it is any other browser it will just fire straight away. I cannot use a jQuery utility like $.Browser and need it to be the tiniest possible method. I hate to use any form of browser detection but it appears as though its my only solution. That said if anyone can come up with another way, that would be fantastic.
Any suggestions would be gratefully received.
You could use JScript conditional compilation, which is only available in IE browsers (up to IE10).
Because it's a comment, it's best to place it inside new Function as minifiers might remove it, changing your code. Though in general you should avoid using new Function, in this case there's not really any other way to prevent minifiers from removing it.
Example:
var isIE = !(new Function('return 1//#cc_on &0')());
However, it seems that your main issue is that the DOM hasn't loaded yet -- make sure that it has loaded before running any loader using the DOMContentLoaded event (IE9+):
document.addEventListener('DOMContentLoaded', function () {
// perform logic here
});
Here is just another solution as the solution from Qantas might not always work. For instance on UMTS connections it could happen that providers remove comments to save bandwith (maybe they preserve conditional comments):
if(navigator.appName == 'Microsoft Internet Explorer'
&& doc.readyState !== 'complete'){
...
}

Categories

Resources