How can I inject JavaScript file into a WebEngineView page? - qt

I'm adding a script tag to a web page once it's fully loaded in a WebEngineView, but it's silently failing somehow.
I inject the script by invoking webview.runJavaScript with this code:
var s = document.createElement('script');
s.src = "qrc:/jquery-2.1.4.min.js";
document.body.appendChild(s);
That's perfectly standard and to a certain extent it works as expected, i.e., if I view the html source of the page, the script tag has indeed been appended to the body.
The problem is that the script isn't being downloaded, or isn't being evaluated, or something. All I know is in the above example the jQuery functions aren't available. If I load a small JavaScript test file with one global variable, that variable's not available either. Changing the url to http instead of qrc and pointing it to a web server makes no difference.
Injecting an img tag works fine; the image is loaded and displayed.
But JavaScript is broken somehow. Does anyone know how to fix this?

The problem had to do with the asynchronous nature of QML and JavaScript.
I was inserting a script tag to inject jQuery, and then I was calling a function to de-conflict my inserted version of jQuery from whatever version of jQuery might already be in the original page.
But I believe the webview had not finished parsing the inserted jQuery library before my de-conflicting function was called, so it failed. (I'm not very experienced with browser programming or I might have suspected this from the beginning.)
The solution was to insert a script tag with a small bit of JavaScript that inserts jQuery and then sets a timeout to wait 200ms before calling the de-conflict function. Like so:
function insertAuhJQuery(){
var s = document.createElement("script");
s.src = "qrc:/jquery-2.1.4.min.js";
document.body.appendChild(s);
window.setTimeout(deConflictJQuery, 200);
}
function deConflictJQuery(){
auh = {};
auh.$ = jQuery.noConflict(true);
}
insertAuhJQuery()
That works reliably and is acceptable for my purpose.

Related

How to debug JS code with breakpoints if the code is inserted via GTM

In GTM I have added a custom html:
<!--Start of Tawk.to Script-->
<script type="text/javascript">
var Tawk_API=Tawk_API||{}, Tawk_LoadStart=new Date();
///////////////
// Chat started
function notifyFacebookAboutAboutChatStarted(content_category){
fbq('track', 'Contact', {how: "chat_started", content_category: content_category}); // Breakpoint
}
function notifyAllSystemsAboutChatStarted(content_category){
notifyFacebookAboutAboutChatStarted(content_category);
}
//////////////
// Chat form submit
function notifyFacebookAboutChatFormSubmit($element, content_category){
fbq('track', 'Lead', {content_category: content_category});
}
function notifyAllSystemsAboutChatFormSubmit(content_category){
notifyFacebookAboutChatFormSubmit($element=$element, content_category=content_category);
}
// Event listeners
Tawk_API.onChatStarted = function(data){notifyAllSystemsAboutChatStarted(content_category=content_category);};
Tawk_API.onOfflineSubmit = function(data){notifyAllSystemsAboutChatFormSubmit(content_category=content_category);};
(function(){
var s1=document.createElement("script"),s0=document.getElementsByTagName("script")[0];
s1.async=true;
s1.src='https://embed.tawk.to/6131ae1fd6e7610a49b36846/1fel10b81';
s1.charset='UTF-8';
s1.setAttribute('crossorigin','*');
s0.parentNode.insertBefore(s1,s0);
})();
</script>
<!--End of Tawk.to Script-->
Please, don't pay much attention to this code because the question is not about it. It is just a piece of code to show what I usually put into GTM containers rather than to be analysed.
And I want to debug this JavaScript in Chrome. In other words I'd like to be able to stop at the breakpoint (marked in the code as // Breakpoint).
I mean in Chrome DevTools panel. Like this:
But this is some code inserted without GTM. I open Sources tab and try to find my JavaScript addeb via GTM there.
Tawk is an online chat I have added via GTM. And it is definitely there as the icon of Tawk has appeared on the site.
So, I'd like to debug my JavaScript code by stopping at breakpoints. But I fail to find where to put those breakpoints. Whether it is possible at all? Or should I debug the code first outside GTM and then insert it inside GTM? But this is clumsy: if users report an error, I'd like to first analyse the problem without changing anything.
So, the question: how to debug JS code with breakpoints if the code is inserted via GTM?
If you put this in a Custom HTML tag it will be inserted as string and run through eval at runtime, so I doubt the debugger will even notice this is JavaScript.
Basically your alternatives are
put it in an external file, which kind of defeats the purpose of GTM
rewrite the code as a custom template (which will require a lot of effort, but means that at least you can write tests inside GTM to make sure your code runs. However custom template code is transpiled, so it might be hard to identify in the debugger)
I think your best choice is actually to test outside GTM first (which I admit is not a real test, since this means you do not test the configuration that will run in the end).

jquery .load( ) and trigger function AFTER new content loads? just like JavaScript onload event

Using jquery, I am swapping some content in a web page by use of jquery's .load() function. I want to trigger an event immediately once the content has actually been loaded, but not before and not after. I'm up for any graceful solution! Why? In this instance, I'm doing the old "fade out, swap content, fade in" approach. My problem? I want to fade back in AS SOON AS the new content is loaded.
Caveats:
Using a callback function as in $('#object').load(url, callback) triggers as soon as .load() function successfully executes (before the content is actually loaded). Useless here.
Using a timed delay for fading back in is not a graceful solution. Very "clunky", especially for those with faster Internet connectivity.
JavaScript's onload event trigger does not work, as the element that .load() is altering has already loaded into the HTML DOM.
jquery's .ready() function also does not work, as the actual element is already loaded.
I do not want to create an onload or .ready() sub-container element, because that's a workaround for what I'm actually trying, though it might be as graceful or more.
How can I fire a function when (and only when) the new .load() content is finally loaded, just like JavaScript's onload event does? Thanks much.
EDIT As it turns out, the jquery .load() function is working flawlessly, and I'm approaching this wrong.
Once the .load() function completes successfully, it calls any "callback" function included by the programmer, just like any other jquery function that accepts a callback as one of its "arguments".
The .load() function is complete once it either errors or successfully begins the HTML replacement and loading of new content, but that is IT! The content will then take however long it takes to load, but your .load call is already complete before that. Therefore, expecting the callback to run after the .load content has loaded will only disappoint you. ;)
I hope others can learn from this just as I did, including those who thought what I thought was the case. Proof: as stated in the jquery ajax .load page, the callback is executed when the request completes, not when the load completes. Eureka. Whoops. /EDIT
Try using a different method rather than load(), I would suggesting using get(). Something like this may be more useful to you...
var jqxhr = jQuery.get(url,vars);
jqxhr.success(function(data){
# This will only be called once the remote content has been loaded in
# The data will then be stored in the data param and can be used within your site
});
jqxhr.error(function(data){
# Something went wrong, never mind lets just handle it gracefully below...
});
I hope this is a solution to your problem!
For more information see http://api.jquery.com/jQuery.get/
I have quickly created this function below that may be of help to you... its not refined!
jQuery.fn.loadNewData = function() {
var object = jQuery(this);
var jqxhr = jQuery.get(arguments[0]);
// check for success
jqxhr.success(function(data) {
// load the data in
object.html(data);
});
jqxhr.error(function(){
alert('Failed to load data');
});
}
Using this you can call how similarly to how you would call the load() function.
jQuery('#object').loadNewData(url);
I think you might be misinterpreting what you are seeing. Depending on the browser you are using you won't see the new elements in the browser if you pop up an alert in the callback because it won't rerender the DOM until you cede control back to the browser. That doesn't mean you can't grab the new elements from the DOM and start fading them in. Take the following jsfiddle: http://jsfiddle.net/ttb55/8/ in Chrome it will show the first div when the second alert is up, then fade in the second div. In IE it won't show the first div when the second alert is up, this is the state I think you are in after load during the callback, but it still works once you hit ok because everything was in the DOM as promised.
Upon reading the jQuery docs pages for jQuery.get() and jQuery.load(), the callback argument is quoted as the following:
"A callback function that is executed if the request succeeds."
Let me stress the terms "request" and "succeeds". The request may succeed, but that does not mean that the content is loaded. Same problem as .load() — the functions aren't built the way I was thinking.
If I want to trigger an event once the new content finally loads, I'll need to take a different approach.
I could use the JS onload event, and trigger it by completely replacing an HTML element (having the replaced code contain an onload property). EDIT: Note that using HTML iframe elements is pretty awful, primitive, and "clunky". I just need to find a better way to trigger a function as soon as loading the new content finishes.
Also, I could use jQuery to check the .ready() state of new content, but ("AFAIK" / as far as I know) that will only work if the checked content is a new HTML element, not a preexisting HTML element whose interior content is changed. The jQuery .ready() status of any preexisting element will (AFAIK) already be shown as "ready" despite if new content is loading. Perhaps I am wrong about this, and I would like to be corrected if so.
Unless otherwise notified, this answer will be marked as the correct one. The original question was mistaken that .load() was all I needed. Cheers!

Is it mandatory to write ready function every time while doing jquery?

Is it mandatory to write $(document).ready(function () {... }) every time ?
Can't we do it without this line?
The reason for placing your code inside this function is that it will get called once the DOM has loaded - meaning that all the elements are accessible. Calling jQuery selectors without this function means that the elements have not necessarily been loaded into the DOM and might not be accessible (and you'll see weird results or nothing at all from your code).
So in essense, yes, it is necessary.
$(document).ready makes sure your code runs when the document is ready (i.e. fully loaded). If you don't need to interact with the document, you don't need this. If you put your Javascript at the end of the document, you probably don't need it either. You should put your code into a function () { } though to namespace it either way.
$(document).ready means the code inside this box will be executed once the all document is ready (loaded). It is considered as safe programming but not mandatory.
For example you call a function in script tag do_something(); and this function is in a js file which is not loaded yet then you will get javascript error.
If you put function like this
$(document).ready(function () {
do_something();
});
you are making sure that when function get called all js files will be there to server.
If you don't use that line, and just include the javascript in your body, it will execute as soon as it's loaded. If it's trying to act on DOM elements that have not yet loaded, unpredictable results will occur.... better to be safe than sorry.
jQuery's ready() function is run after the page's content is loaded. This is relatively equivalent to using <body onload="function1();function2();">
If you want to call multiple functions when the page is done loading, you can do the following:
$(document).ready(function() {
function1();
function2();
});
In order to use javascript, you must call it somewhere. This can be in body "onload", jQuery's ready() function, or an event, like a mouse click event.
No you don't always have to do this. You only use it if you want to make sure whatever is inside the ready function loads before the page is displayed in the browser. If you do not care to load the script before page load, then you can just put the script at the end of the page before the closing body tag.
Also As a shortcut to $(document).ready(function () you can do $(function()

ajaxSubmit and Other Code. Can someone help me determine what this code is doing?

I've inherited some code that I need to debug. It isn't working at present. My task is to get it to work. No other requirements have been given to me. No, this isn't homework, this is a maintenance nightmare job.
ASP.Net (Framework 3.5), C#, jQuery 1.4.2. This project makes heavy use of jQuery and AJAX. There is a drop down on a page that, when an item is chosen, is supposed to add that item (it's a user) to an object in the database.
To accomplish this, the previous programmer first, on page load, dynamically loads the entire page through AJAX. To do this, he's got 5 div's, and each one is loaded from a jQuery call to a different full page in the website.
Somehow, the HTML and BODY and all the other stuff is stripped out and the contents of the div are loaded with the content of the aspx page. Which seems incredibly wrong to me since it relies on the browser to magically strip out html, head, body, form tags and merge with the existing html head body form tags.
Also, as the "content" page is returned as a string, the previous programmer has this code running on it before it is appended to the div:
function CleanupResponseText(responseText, uniqueName) {
responseText = responseText.replace("theForm.submit();", "SubmitSubForm(theForm, $(theForm).parent());");
responseText = responseText.replace(new RegExp("theForm", "g"), uniqueName);
responseText = responseText.replace(new RegExp("doPostBack", "g"), "doPostBack" + uniqueName);
return responseText;
}
When the dropdown itself fires it's onchange event, here is the code that gets fired:
function SubmitSubForm(form, container) {
//ShowLoading(container);
$(form).ajaxSubmit( {
url: $(form).attr("action"),
success: function(responseText) {
$(container).html(CleanupResponseText(responseText, form.id));
$("form", container).css("margin-top", "0").css("padding-top", "0");
//HideLoading(container);
}
}
);
}
This blows up in IE, with the message that "Microsoft JScript runtime error: Object doesn't support this property or method" -- which, I think, has to be that $(form).ajaxSubmit method doesn't exist.
What is this code really trying to do? I am so turned around right now that I think my only option is to scrap everything and start over. But I'd rather not do that unless necessary.
Is this code good? Is it working against .Net, and is that why we are having issues?
A google search for
jquery ajax submit
reveals the jQuery Form Plugin. Given that, is that file included on your page where the other code will have access to the method? Does this code work in Firefox and not IE?
Seems like there was too much jQuery fun going on. I completely reworked the entire code block since it was poorly designed in the first place.

Using JQuery as an ASP.NET embedded webresource

I have an ASP.NET server control which relies on JQuery for certain functionality. I've tried to add as a webresource.
My problem is my method of including the jquery file adds it to the body, or the form to be exact:
this.Page.ClientScript.RegisterClientScriptInclude(...)
The alternative to this is to add it as a literal in the head tag:
LiteralControl include = new LiteralControl(jslink);
this.Page.Header.Controls.Add(include);
The problem with this however is any existing code srcs in the head which use JQuery fail, as JQuery is loaded afterwards (ASP.NET adds the literal at the bottom of the control tree).
Is there a practical way of making JQuery an embedded resource, but loaded in the head first? Or should I give up now.
If you want to package up jQuery and embed it inside your own server control you should serve it to the client using the ScriptManager. From the top of my head you have to:
add jQuery.js to your project
under its "Build Action" Property,
make it an Embedded Resource
in the AssemblyInfo.cs for your
control add
[assembly: WebResource("<Your Server Control namespace>.jQuery.js", "application/x-javascript")]
Make your control inherit from
System.Web.UI.ScriptControl (or at
least implement IScriptControl)
Override GetScriptReferences:
protected override IEnumerable<ScriptReference>
GetScriptReferences()
{
return new ScriptReference[] {
new ScriptReference("<Your Server Control namespace>.jQuery.js", this.GetType().Assembly.FullName),
};
}
All of your own client script should be setup inside:
protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
Which will then ensure the correct order of dependencies (ie jQuery will be available to your own client script).
Update:
A far easier way of doing it is to simply add the script tag dynamically, in your script and point to the google code hosting. e.g.
function include_dom(script_filename) {
var html_doc = document.getElementsByTagName('head').item(0);
var js = document.createElement('script');
js.setAttribute('language', 'javascript');
js.setAttribute('type', 'text/javascript');
js.setAttribute('src', script_filename);
html_doc.appendChild(js);
return false;
}
include_dom("http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js");
The function is taken from this article
Crecentfresh pushed me in the right direction, I also found
http://en.csharp-online.net/Creating_Custom_ASP.NET_AJAX_Client_Controls—IScriptControl.GetScriptReferences_Method.
My problem still remains though, the ScriptManager adds the references after the script in the head but I think this is an issue that can't be resolved. I've opted to answer myself but also upvoted crescentfresh.

Resources