How to use jQuery in HtmlUnit executeJavaScript(query) argument? - javascript

I extensively use the "executeJavaScript(String queryText)" method of the HtmlPage class (HTMLUnit). It works nice, but it does not recognize jQuery or $ symbols inside queryText. I assume I need either initialize the internal jQuery support or include jQuery library. Does anybody know how to do this?
Thank you

Make sure you have script tag that downloads jquery script before you execute that statement. Best place for it is within the head element of the page. Another possibility is that you have another third-party script redefining $ (it's a common thing): then you can try using jQuery in place of $ within your query-text.

String jQueryInjector = 'eval(
function loadJQuery(){
var newJQueryLib = document.createElement("script");
newJQueryLib.type = "text/javascript";
newJQueryLib.src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1
/jquery.min.js";
(document.getElementsByTagName("head")[0]||document
.getElementsByTagName("body")[0])
.appendChild(newJQueryLib););
loadJQuery();';
ScriptResult sr = page.executeJavaScript(jQueryInjector);

Related

Including dynamic script in jQuery [duplicate]

Any idea why the piece of code below does not add the script element to the DOM?
var code = "<script></script>";
$("#someElement").append(code);
The Good News is:
It's 100% working.
Just add something inside the script tag such as alert('voila!');. The right question you might want to ask perhaps, "Why didn't I see it in the DOM?".
Karl Swedberg has made a nice explanation to visitor's comment in jQuery API site. I don't want to repeat all his words, you can read directly there here (I found it hard to navigate through the comments there).
All of jQuery's insertion methods use
a domManip function internally to
clean/process elements before and
after they are inserted into the DOM.
One of the things the domManip
function does is pull out any script
elements about to be inserted and run
them through an "evalScript routine"
rather than inject them with the rest
of the DOM fragment. It inserts the
scripts separately, evaluates them,
and then removes them from the DOM.
I believe that one of the reasons jQuery
does this is to avoid "Permission
Denied" errors that can occur in
Internet Explorer when inserting
scripts under certain circumstances.
It also avoids repeatedly
inserting/evaluating the same script
(which could potentially cause
problems) if it is within a containing
element that you are inserting and
then moving around the DOM.
The next thing is, I'll summarize what's the bad news by using .append() function to add a script.
And The Bad News is..
You can't debug your code.
I'm not joking, even if you add debugger; keyword between the line you want to set as breakpoint, you'll be end up getting only the call stack of the object without seeing the breakpoint on the source code, (not to mention that this keyword only works in webkit browser, all other major browsers seems to omit this keyword).
If you fully understand what your code does, than this will be a minor drawback. But if you don't, you will end up adding a debugger; keyword all over the place just to find out what's wrong with your (or my) code. Anyway, there's an alternative, don't forget that javascript can natively manipulate HTML DOM.
Workaround.
Use javascript (not jQuery) to manipulate HTML DOM
If you don't want to lose debugging capability, than you can use javascript native HTML DOM manipulation. Consider this example:
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "path/to/your/javascript.js"; // use this for linked script
script.text = "alert('voila!');" // use this for inline script
document.body.appendChild(script);
There it is, just like the old days isn't it. And don't forget to clean things up whether in the DOM or in the memory for all object that's referenced and not needed anymore to prevent memory leaks. You can consider this code to clean things up:
document.body.removechild(document.body.lastChild);
delete UnusedReferencedObjects; // replace UnusedReferencedObject with any object you created in the script you load.
The drawback from this workaround is that you may accidentally add a duplicate script, and that's bad. From here you can slightly mimic .append() function by adding an object verification before adding, and removing the script from the DOM right after it was added. Consider this example:
function AddScript(url, object){
if (object != null){
// add script
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "path/to/your/javascript.js";
document.body.appendChild(script);
// remove from the dom
document.body.removeChild(document.body.lastChild);
return true;
} else {
return false;
};
};
function DeleteObject(UnusedReferencedObjects) {
delete UnusedReferencedObjects;
}
This way, you can add script with debugging capability while safe from script duplicity. This is just a prototype, you can expand for whatever you want it to be. I have been using this approach and quite satisfied with this. Sure enough I will never use jQuery .append() to add a script.
I've seen issues where some browsers don't respect some changes when you do them directly (by which I mean creating the HTML from text like you're trying with the script tag), but when you do them with built-in commands things go better. Try this:
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = url;
$("#someElement").append( script );
From: JSON for jQuery
It is possible to dynamically load a JavaScript file using the jQuery function getScript
$.getScript('http://www.whatever.com/shareprice/shareprice.js', function() {
Display.sharePrice();
});
Now the external script will be called, and if it cannot be loaded it will gracefully degrade.
What do you mean "not working"?
jQuery detects that you're trying to create a SCRIPT element and will automatically run the contents of the element within the global context. Are you telling me that this doesn't work for you? -
$('#someElement').append('<script>alert("WORKING");</script>');
Edit: If you're not seeing the SCRIPT element in the DOM (in Firebug for example) after you run the command that's because jQuery, like I said, will run the code and then will delete the SCRIPT element - I believe that SCRIPT elements are always appended to the body... but anyway - placement has absolutely no bearing on code execution in this situation.
This works:
$('body').append($("<script>alert('Hi!');<\/script>")[0]);
It seems like jQuery is doing something clever with scripts so you need to append the html element rather than jQuery object.
Try this may be helpful:
var fileref=document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src","scriptAnalytics.js");
document.getElementsByTagName("head")[0].appendChild(fileref);
I want to do the same thing but to append a script tag in other frame!
var url = 'library.js';
var script = window.parent.frames[1].document.createElement('script' );
script.type = 'text/javascript';
script.src = url;
$('head',window.parent.frames[1].document).append(script);
<script>
...
...jQuery("<script></script>")...
...
</script>
The </script> within the string literal terminates the entire script, to avoid that "</scr" + "ipt>" can be used instead.
Adding the sourceURL in the script file helped as mentioned in this page:
https://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
In the script file, add a statement with sourceURL like "//# sourceURL=foo.js"
Load the script using jQuery $.getScript() and the script will be available in "sources" tab in chrome dev tools
Your script is executing , you just can't use document.write from it. Use an alert to test it and avoid using document.write. The statements of your js file with document.write will not be executed and the rest of the function will be executed.
This is what I think is the best solution. Google Analytics is injected this way.
var (function(){
var p="https:" == document.location.protocol ? "https://" : "http://";
d=document,
g=d.createElement('script'),
s=d.getElementsByTagName('script')[0];
g.type='text/javascript';
g.src=p+'url-to-your-script.js';
s.parentNode.insertBefore(g,s); })();
You don't need jQuery to create a Script DOM Element. It can be done with vanilla ES6 like so:
const script = "console.log('Did it work?')"
new Promise((resolve, reject) => {
(function(i,s,o,g,r,a,m){
a=s.createElement(o),m=s.getElementsByTagName(o)[0];
a.innerText=g;
a.onload=r;m.parentNode.insertBefore(a,m)}
)(window,document,'script',script, resolve())
}).then(() => console.log('Sure did!'))
It doesn't need to be wrapped in a Promise, but doing so allows you to resolve the promise when the script loads, helping prevent race conditions for long-running scripts.
Append script to body:
$(document).ready(function() {
$("<script>", { src : "bootstrap.min.js", type : "text/javascript" }).appendTo("body");
});
Another way you can do it if you want to append code is using the document.createElement method but then using .innerHTML instead of .src.
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.innerHTML = 'alert("Hey there... you just appended this script to the body");';
$("body").append( script );
I tried this one and works fine. Just replace the < symbol with that \x3C.
// With Variable
var code = "\x3Cscript>SomeCode\x3C/script>";
$("#someElement").append(code);
or
//Without Variable
$("#someElement").append("\x3Cscript>SomeCode\x3C/script>");
You can test the code here.
Can try like this
var code = "<script></" + "script>";
$("#someElement").append(code);
The only reason you can't do "<script></script>" is because the string isn't allowed inside javascript because the DOM layer can't parse what's js and what's HTML.
I wrote an npm package that lets you take an HTML string, including script tags and append it to a container while executing the scripts
Example:
import appendHtml from 'appendhtml';
const html = '<p>Hello</p><script src="some_js_file.js"></script>';
const container = document.getElementById('some-div');
await appendHtml(html, container);
// appendHtml returns a Promise, some_js_file.js is now loaded and executed (note the await)
Find it here: https://www.npmjs.com/package/appendhtml
Just create an element by parsing it with jQuery.
<div id="someElement"></div>
<script>
var code = "<script>alert(123);<\/script>";
$("#someElement").append($(code));
</script>
Working example: https://plnkr.co/edit/V2FE28Q2eBrJoJ6PUEBz

Get the referral <script> object in JS in the source script

Assuming we have a script tag in html:
<script mycustom-attribute="sample-value" src="http://example.com/myscript.js"></script>
Now, is there anyway, where we can get the caller script object in "myscript.js" so that we can read mycustom-attribute. Of course we can use some id/class, but what if we want to refer it without its name/id/class. The idea is to use to embed widget without having a problem of conflicting id/name/class.
You can select the script which has the custom attribute:
var script = document.querySelector('script[mycustom-attribute]');
if (script) {
var value = script.getAttribute('mycustom-attribute');
console.log(value);
}
Since you have a custom attribute to work with, see Neil's answer.
If there could be multiple scripts with the custom attribute, or for those who don't have a custom attribute to work with:
Assuming your script tag is as shown, without async or defer, then in top-level code in that script, you know reliably that it's the last script in the DOM, so:
var scripts = document.getElementsByTagName("script");
var value = scripts[scripts.length - 1].getAttribute("mycustom-attribute");
If there may be an async or defer, you need to look by src, perhaps using the "ends with" attribute selector ($=):
var script = document.querySelector("script[src$='myscript.js']");
var value = script.getAttribute("mycustom-attribute");
(There's also document.currentScript, but it's "...fallen out of favor in the implementer and standards community...".)
Side note: When using custom attributes, use the data- prefix.

Adding jQuery to a 3rd party page fails

I am trying out a few things, and among those I tried to insert jquery on a site, namely, http://www.tesco.com/wine.
For some reason, I was not able to able access jQuery even though I was able to successfully append a new script tag to the body element. Also, the page seems to have a window.$ function that I tried to delete with delete window.$. This, seems to return false for me. How do you make something undeleteable?
Here is the code I used to append the jQuery script to the document:
var s = document.createElement('script');
s.src = "http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js";
document.body.appendChild(s)
It is working on other pages.
After discussing this in JavaScript chat, Tim Stone discovered that the JS on the page adds its own implementation of Object.prototype.extend - which breaks the jQuery script. To fix it (but potentially break another script on the page), you can delete that before adding jQuery:
delete Object.prototype.extend;
var s = document.createElement('script');
s.src = "http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js";
document.body.appendChild(s)
$ is already defined on that page with the following function declaration:
function $(A){return document.getElementById(A)}
It is not uncommon to alias document.getElementById() with $() — in fact, Firebug and WebKit's developer tools do this in their console.
It is not deletable, because it is declared as a function statement and not an object property. While delete may work in some browsers, it shouldn't and won't in others. That being said, I was able to override the function with a simple assignment:
$ = function () {}
When jQuery loads, it creates the jQuery namespace and aliases this namespace with $. Therefore, if something else on the page is overriding $, you can still use jQuery().
If $ is already defined, you can always use jQuery instead.

how to assign a block of html code to a javascript variable (to invoke a javascript call)

I want to assign a block of html code to a js variable
the html code is
<script>alert('test');</script>
I want to do this
var script = "<script>alert('test');</script>";
document.getElementById('test').innerHTML = script;
but it does not work
if I substitute the tag (or any other tag) for the tag, then it works.
var script = "<span>alert('test');</span>";
I tried
var script = "<script>alert('test');</script>";
eval(script);
and
eval ("<span>alert('test');</span>");
but they both have syntax errors.
It probably has to do with have script tags inside of script tags
<script> <script> </script> </script>
Is there a way to get around this?
TIA
You cannot inject a <script> element using .innerHTML and expect it to evaluate. You must use either eval or document.write or inject the <script> into the DOM the "normal" way. With dynamic scripts, eval is recommended.
Remember that eval evaluates pure JavaScript and does not use the HTML interpreter. (Contrast this with PHP's default behaviour, where eval is like inserting ?>$string<?php into the document.)
Also remember that a script terminates when it approaches </script>. It's strange to be inserting </script> into JavaScript anyway (unless you're using the document.write method, which has many problems and should be avoided where size isn't an extreme issue).
Here's an example of using eval:
var script = 'alert("test");';
eval(script);
If you want to inject a script into the DOM with other elements, you need to extract the <script> element after injecting and execute the (internal or external) script. Frameworks like jQuery and Prototype do this automatically, which is one of the many reasons why they are recommended over vanilla JavaScript.
The way to do this with pure DOM methods I believe is as follows.
var script = document.createElement("script");
script.type = "text/javascript";
script.innerHTML = "alert('foo!')";
document.body.appendChild( script );
Works for me™.

Dynamically including javascript files only once

I have a javascript function I'm writing which is being used to include an external JS file, but only once. The reason I need such a function is because it is being called when some content is loaded via AJAX and I need to run page-specific code to that content (no, just using .live won't cover it).
Here's my attempt, shortened for brevity:
$.include_once = function(filename) {
if ($("script[src='" + filename + "']").length === 0) {
var $node = $("<script></script>")
.attr({
src : filename,
type : "text/javascript"
})
;
$(document.body).append($node);
}
};
This works fine: the function is called, it loads the external file, and that file is being run when loaded. Perfect.
The problem is that it will always re-load that external file: the query I'm using to check for the presence of the script always finds nothing!
When debugging this, I added some lines:
alert($("script").length); // alerts: 4
$(document.body).append($node);
alert($("script").length); // alerts: 4
Looking in the dynamic source (the HTML tab of Firebug), I can't find the script tag at all.
I know that I could maintain an array of files that I've previously included, but I was hoping to go with a method such as this, which (if it worked), seems a bit more robust, since not all the JS files are being included in this way.
Can anyone explain the behaviour seen in this second snippet?
jQuery is a bit of a dumb-dumb in this case; it doesn't do at all what you'd expect. When you append($node) jQuery does this:
jQuery.ajax({
url: $node.src,
async: false,
dataType: "script"
})
Woops! For local files (eg on the same domain) jQuery performs a standard XMLHttpRequest for the .js file body, and proceeds to "eval" it by a whole convoluted process of creating a <script> tag (again!) and settings it's contents to your .js file body. This is to simulate eval but in the global context.
For cross-domain files, since it cannot perform the standard XMLHttpRequest due to the same-domain policy, jQuery once again creates a <script> element and inserts it into <head>.
In both the local and cross-domain cases above jQuery finally gets around to doing this:
head.removeChild(script);
And booms your .length check! Bummer.
So on to your problem, don't bother jQuery with this. Just do
document.getElementsByTagName('head')[0]
.appendChild(
document.createElement('script')
)
.src = filename;
Which will do what you'd expect, particularly wrt querying for it later.
You're trying to solve a problem that has already been solved several times over. Try LazyLoad for example. There are also similar plugins for jQuery.
Instead of setting the source attribute of the script-tag, set the "text" attribute of the script tag. This works in all modern browsers (the application where I use that in practice does not support IE6, so I do not know about this creep...).
In practice it would look like this (you HAVE TO add code to omit double inclusion on yourself - e.g. a simple array of all alread loaded scripts, though thats very application specific, and why should you anyway load code twice? try to omit double-loading code...!):
var script_source_code_string = <some dynamically loaded script source code>;
var $n = $("<script></script>");
$n.get(0).text = script_source_code_string;
$(document.body).append($n);
Or even simpler (without jquery, my code at this stage does not know jquery, it may also be loaded dynamically):
var script_source_code_string = <some dynamically loaded script source code>;
var s = document.createElement('script');
document.getElementsByTagName('head')[0].appendChild(s);
s.text = script_source_code_string;

Categories