Avoid requesting the same external script twice - javascript

I request an external script by adding this to my HTML file:
<script>
$(document).on("turbolinks:load", function() {
$.getScript("https://example.com/script");
});
</script>
Say the content of the script is as follows:
doSomething = function() {
// ...
};
My website is a Ruby on Rails app with Turbolinks, which caches the content of the requested script between page visits. My script tag does not know about this, so if I revisit the page it will request the script again. How do I avoid this? My current solution is to check if the scripts content is known:
<script>
$(document).on("turbolinks:load", function() {
if (!window.doSomething) {
$.getScript("https://example.com/script");
}
});
</script>
But this depends on the content inside the script staying the same. So I would rather check if a script from the source https://example.com/script already exists? Or maybe some other approach. Any ideas?

Like epascarello commented, $.getScript appends to head so check that it's not in head before getting it.
if (!$('head script[src^="https://example.com/script"]').length){
$.getScript("https://example.com/script");
}
Use the Attribute Starts With Selector because $.getScript appends a timestamp to avoid getting an already cached version, i.e. it requests a new URL each time.
If you're going to use it more than once:
function getScriptOnce(url){
let selector = 'head script[src^="' + url + '"]';
if (!$(selector).length){
$.getScript(url);
}
}
getScriptOnce("https://example.com/script");

Related

Can't manage to get JavaScript function running

I wrote the following function and put it between script tags in the header of my wordpress website.
function turnShopBlue(){
var location = window.location.href;
if(location === "MY URL GOES HERE"){
var menuItem = document.getElementById("menu-item-3352");
menuItem.classList.add("current-menu-item");
//document.querySelector("#menu-item-3352").addClass("current-menu-item");
}
}
turnShopBlue();
But for some reason the function doesn't run.
Each time I go the URL (page) that I choose in "MY URL GOES HERE", the class current-menu-item doesn't get added. What do I miss?
I've already tried with triple === and double ==, without success.
EDIT SOLUTION:
My mistake was that I placed the script in the header, the problem here is that the script then runs before the entire DOM is rendered. So alot of the variables I instantiate don't have the good values.
The problem was solved by loading the script in the header;
Also, I had to put it in a jQuery document ready function to autoload it on pageload.
Always put your Javascript code at the end (footer) of your body tag. This way, all your HTML will be parsed before any Javascript execution, further, your external scripts must be placed there as well to improve page's loading performance.
<html>
<body>
<tag-name id='menu-item-3352'></tag-name>
<script>
function turnShopBlue() {
var location = window.location.href;
if (location === "MY URL GOES HERE") {
var menuItem = document.getElementById("menu-item-3352");
menuItem.classList.add("current-menu-item");
//document.querySelector("#menu-item-3352").addClass("current-menu-item");
}
}
turnShopBlue();
</script>
<script external Javascript resources></script>
<script external Javascript resources></script>
</body>
</html>
Resource
$(document).ready equivalent without jQuery

javascript not loaded from django template when URL is invoked from jQuery load()

I have a django generated page, and in it I have a jQuery on click handler that loads another django page using the jQuery load() function:
$("#loadit").on("click", function() {
load_it($("#loadit"), url);
});
function load_it(el, url)
{
var el_wf = $('<div />');
el_wf.load(url, function (html, status) {
el.children().remove();
el_wf.show();
el_wf.appendTo(el);
});
}
In that second django template I have some code like this:
<script type="text/javascript" src="/static/scripts/myscript.js"></script>
.
.
.
<div>
<script>
function_in_myscript();
</script>
</div>
When I click on the element in the first page, and the second page is loaded that js function is not invoked. There are no errors, and the rest of the template is run and the page is generated.
But if I go to the second URL directly from my browser the js function is run.
Is there something with load() that is preventing this from working?
JavaScript inserted as DOM text will not execute. The W3C Specification for XMLHttpRequest states: Scripts in the resulting document tree will not be executed, resources referenced will not be loaded and no associated XSLT will be applied.
The solution is to call JavaScript’s eval() function on the text of the script. Using jQuery it is very easy to iterate through the collection of script tags and to eval() contents of the TextNode. However this looks like a bad practice.
Using script tag in second html is not a good way of loading a new js file. I suggest that you use RequireJS for this purpose. RequireJS takes a different approach to script loading than traditional tags. It can run fast and optimize well.
Here is an example to demonstrate the usage, In first File:
<script type="text/javascript" src="/static/scripts/require.js"></script>
.
.
.
<script type="text/javascript">
require.config({
paths: {
myscript: '/static/scripts/myscript'
}
});
$("#loadit").on("click", function() {
load_it($("#loadit"), url);
});
function load_it(el, url)
{
var el_wf = $('<div />');
el_wf.load(url, function (html, status) {
el.children().remove();
el_wf.show();
el_wf.appendTo(el);
require(['myscript'], function(myscript) {
function_in_myscript();
});
});
}
</script>
In that second django template just load the html code. No need for loading script.

Intercepting script load

What I need is to hook/intercept other external JS load.
I can place js anywhere in document
Example:
<script src="hook.js"></script>
<script src="a.js"></script>
<script src="b.js"></script>
Hook.js should intercept a.js and b.js. Problem is, that when hook.js is executed, i cannot see other scripts (document.scripts contains only hook.js) and document ready event is too late (scripts a.js and b.js are executed).
Is there any way to "see" other script tags, before are executed ?
Thanks for any help
Edit
I need to do any "magic" inside hook.js without modyfing (statically) other HTML.
No jQuery
Credit goes here: https://stackoverflow.com/a/59424277/2016831
You can use a MutationObserver to see what elements are being added to the DOM, and when they are being added, simply change the source code, or if its referencing another URL, just redirect it to your own server, with the original URL as a get parameter instead, and return the modified code that way.
Based on the above answer, you could do something like this:
<script>
new MutationObserver((m, o) => {
let potentialScript = document.querySelector("script + script");
console.log(potentialScript.textContent);
if(potentialScript) {
o.disconnect();
potentialScript
.textContent =
potentialScript
.textContent
.replace(
"})()",
`
window.wow = mySecretMethod;
})()
`
);
}
}).observe(
document.body,
{
childList:1
}
);
</script>
<script>
(function() {
let mySecretMethod = () => {
//does a bunch of evil secret stuff
console.log("HA!");
};
})();
wow()
</script>
<script>
console.log(wow())
</script>
Alternatively you can redirect the HTTP requests with a chrome extension, see https://stackoverflow.com/a/61202516/2016831 for more
If I understand what you're trying to do correctly...
If you can control how scripts A and B are loaded, the best approach is to place them on the same domain as the current page (possibly via proxy), load the files via AJAX, and insert your hooks that way. A library like jQuery as m.casey suggested would make the details of the AJAX and executing the script quite simple.
Otherwise, Javascript does not really have the ability to interact with the parsing of the document (which is what is causing scripts a and b to be loaded in your example, and what would be need to be modified to "intercept" script loading), except by using the evil of document.write to modify the HTML stream. Of course, this only works if hook.js is loaded synchronously (as it is in your example code), if it's loaded into HTML and not XHTML, if you can place a second hook afterwards to postprocess the modified HTML stream, and if you are sure the HTML stream won't escape your mechanism.
For example..
<script id="hook1">document.write("<"+"textarea id='capture'>");</script>
<script src="a.js"></script>
<script src="b.js"></script>
<script id="hook2">document.write("<"+"/textarea");</script>
<script id="hook3">doSomethingWith(document.getElementById("capture").value)</script>
Note that this is a huge hack and you probably shouldn't be doing it.
If you're using jQuery, you could have hook.js load the scripts you wish to intercept as follows:
$.getScript("a.js");
$.getScript("b.js");
This would dynamically create the script tags and you would be certain that hook.js would always proceed a.js and b.js.

remove script tag from ajax response data

i am working on a single page application using jQuery. whole html pages are sent as response to browser as ajax response.
$.post(url, function (data) {
$("#resp").html(data);
$("#resp").find("script").each(function (i) {
//alert($(this).text());
eval($(this).text());
});
});
how to remove script tags from data and than assign html to the div ?
the issue i am facing is the scripts that are written in the response page. they were not getting added to the DOM at first, so i used eval(), now the scripts are getting added twice in some situations.
The easiest way would be to use the .load() function with a fragment selector, since that will strip out <script> tags prior to updating content and result in them not being executed. If you're working with entire HTML pages though there may not be a suitable selector for you to use. However, I'd suggest trying this first:
$('#resp').load(url + ' body');
That would give you just the content between the <body> and </body> tags in the HTML page requested via AJAX.
If that doesn't work, I guess you could try manually stripping out <script> tags from the response prior to adding to the DOM:
$.post(url, function(data) {
var tempDiv = $('<div>').html(data).find('script').remove();
$('#resp').html(tempDiv.html());
});
That creates a new <div> element that isn't part of the document, sets its HTML to the returned HTML from the AJAX request, searches for <script> elements inside that, and then removes them. However, even though the element isn't part of the current document yet, the scripts may still end up being executed (I've never had a reason to do this so I haven't tested it).
with the help of Anthony's answer this is what i did to get it working :
$.post(url, function (data) {
var tempDiv = $('<div>').html(data);
var raw = $('<div>').html(data);
$(tempDiv).find("script").remove();
$("#resp").html(tempDiv.html());
$(scripts).find("script").each(function (i) {
//alert($(this).text());
eval($(this).text());
});
});
i could not understand why
var tempDiv = $('<div>').html(data).find('script').remove();
did'nt work though.

Removing a script tag with a specific content using jquery

I have a page that i dont have access to its an obvius site. I would like to remove a script html tag with a content. For now i have this but is not working. I am using userscripts like coding!
function main(){
var def = $('script[type="text/javascript"]').html();
$('script[type="text/javascript"]').each(function() {
if (def == 'document.write("<scr"+"ipt type=\'text/javascript\' src=\'http://storing.com/javascripts/"+(new Date()).getTime()+"/3e155555e1b26c2d1ced0f645e_1_1.js\'></scr"+"ipt>")')
$('script[type="text/javascript"]').remove();
}
}
UPDATE:
<script type="text/javascript">document.write("<scr"+"ipt type='text/javascript' src='http://somedomain.com/javascripts/"+(new Date()).getTime()+"/3e1a0cd37f25a6e1b26c2d1ced0f645e_1_1.js'></scr"+"ipt>")</script>
This is the whole script what i want to remove... it inserts a div that i am removing right now i just wanted to know if there is any other method. BUt as i see the only is the hosts file thing :)
I don't believe this will work, since a loaded script will already have run.
That said, you probably want something like this:
$('script').each(function() {
if (this.src.substring(0, 31) === 'http://storing.com/javascripts/') {
$(this).remove();
}
});
It's impossible to match the <script> tag based on the output of .html() because that only returns the contents of the element, and not the outer <script> element nor the element's attributes.
When a script is loaded in a page, it is evaluated and executed by the browser immediately after. After the script has been executed, the content of the script tag is irrelevant.
You might be able to achieve what you want by unbinding the events which might have been loaded by the script. Are there any events you want to disable?
If the script is in a certain domain and you want to block all traffic to it, you could add the following entry to your hosts file:
127.0.0.1 storing.com
This will prevent the request to reach it's destination.

Categories