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.
Related
I have multiple JavaScript files which are loaded before page load which are affecting page speed. I want that my JS files to load after page load though to improve page speed.
I tried to use async and defer but due to dependency to each other, this way is not working out for me. I also tried lazy loading through AJAX but that is also not working for me (using this link https://w3bits.com/async-javascript)
#section Scripts {
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=API_KEY&sensor=false&libraries=places"></script>
<script src="/lib/bootstrap-datetimepicker/js/moment.js" type="text/javascript"></script>
<script src="/lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js" type="text/javascript"></script>
<script src="/js/viewcomponent/windowsignup.js"></script>
<script type='text/javascript' src='/js/viewcomponent/kundliregistrationwindow.js' async='async'></script>
}
I use multiple JS files on a page and I want them all to load after page load. Is there any way to do that?
You can use getScript inside document.ready.
$(document).ready(function() {
$.getScript("domainpath/lib/bootstrap-datetimepicker/js/moment.js");
});
It's fetch your js after dom ready.
You can use Jquery's $.getScript() function but then you have to keep an eye on proper loading order. Would recommend more to bundle all necessary files on the server side and only serve a single file
When the browser receives a request for an HTML file the DOM starts to draw. If you place your scripts in the <head> tag the DOM stops from rendering and the browser starts to execute the <script> files.
One solution could be to place your <script> files at the end of the <body>.
Another solution could be to dynamically load your scripts into your page after the page is loaded like this:
window.addEventListener('load', () => {
let script = document.createElement('script');
script.src = './my_lazy_loaded_script.js';
document.body.appendChild(script);
});
If you have multiple files that are dependent on each other you could do something like this using the async/await mechanism:
window.addEventListener('load', async () => {
console.log('page loaded')
try{
await loadFileAsync('./my_first_lazy_load.js');
await loadFileAsync('./my_second_lazy_load.js');
} catch(err){
console.log(err);
} finally {
console.log('Everything loaded')
}
})
const loadFileAsync = (url) => {
return new Promise((resolve, reject) => {
if(url){
let script = document.createElement('script');
script.src = url;
document.body.appendChild(script);
resolve(true);
} else {
reject(false);
}
});
}
If you want to load your scripts as soon as the DOM is rendered, you can replace window.addEventListener('load',()=>{}) with document.addEventListener('DOMContentLoaded', () => {})
You usually use a Scripts section to control the location where your scripts are being rendered in the document. The way browsers work, they will interpret the HTML from top to bottom. If they encounter a <script> tag, they will pause the HTML rendering and interpret the JavaScript first. For external JavaScript files this means that they will be included and interpreted first (if they aren’t marked as defer or async).
As such, a common practice is to add JavaScript at the end of the <body> tag. That way, the scripts will not prevent rendering the document first but they will also execute early enough to be able to immediately have an effect. In addition, since they are executed after the majority of the <body> has already been interpreted, they can directly access DOM elements. So you generally don’t need to listen to another DOM event first before you can do something with the DOM.
For example, this would work just fine:
<body>
<div id="foo"></div>
<script>
var foo = document.getElementById('foo');
</script>
</body>
However, if you had the <script> before the <div> (or inside of the <head>), the script would fail to find the foo element simply because it isn’t added to the DOM yet.
For ASP.NET Core this means that you should make sure that your Scripts section is added at the end of your <body> tag inside your layouts file. So it should look like this:
<!-- (The rest of the layout) -->
#RenderSection("Scripts", required: false)
</body>
</html>
You can also add script tags before the RenderSection call to make sure that those scripts are available on all pages but loaded before other scripts. For example, if you are using Bootstrap and you want to use jQuery, it might make sense to include that globally for all pages. But since you have it before the other scripts, it is available for the other scripts.
As far as dependencies between scripts go, make sure that you include the scripts in the order they need to executed. So if you have script B that depends on functions in script A, make sure to include A before B. But then, without using defer or async, just including them should work just fine.
I'm trying to 'include' some generated HTML code on my page with jQuery load (injecting it into #loadhead div):
<script>
$("#loadhead").load("head.php");
</script>
<script defer>
... rest of the code
</script>
I want the second part of the script to load only after head.php is done loading. I tried to enforce execution of the second script tag with defer, but it deson't seem to work - still, once in a while, head.php doesn't manage to load before the rest of the code. What can I do to ensure it is always loaded completely? It generates some JavaScript values that are used by the 'defer' script.
Two options for you:
1. Use a function you call from load's callback
Put your second script's code in a function, so it's loaded but not run, and then call that function from load's success callback.
Example:
<script>
$("#loadhead").load("head.php", otherStuff);
function otherStuff() {
// ...
}
</script>
(I didn't see any reason they should be in separate script tags, so they aren't.)
I'd probably put that all in a .js file and link it rather than having script embedded in the HTML inline.
2. Load the script when load is done
Don't include the second script tag at all; load the script later in the load success callback, either using $.getScript or appending a script element.
Example:
<script>
$("#loadhead").load("head.php", function() {
$.getScript("otherstuff.js");
});
</script>
(I didn't see any reason they should be in separate script tags, so they aren't.)
I'd very much go with Option #1, to avoid unnecessary delays.
Got a little problem here. Basically, I'm trying to add a script tag after the page loads.
This is what I am doing:
index.php:
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
function getad()
{
$.post('assets/getad.php', "ad", function(response) {
response = response.replace('document.write','document.getElementById("ad").innerHTML = ');
eval(response);
console.log(response);
});
}
getad();
</script>
<div id="ad"></div>
</body>
</html>
getad.php:
<?php
echo file_get_contents("http://ads1.qadabra.com/t?id=a823aca3-9e3c-4ddd-a0cc-14b497cad85b&size=300x250");
?>
You can find a demo here: http://dev.cj.gy/game/
As you can see, the #ad div DOES get filled with the correct script tag, but it doesnt actually run, If I edit the page to include the script tag right at page load, it does run.
Yes, <script> tags cause execution when parsed as part of the main document; they don't execute from being written to innerHTML.
You can create an executing script element outside of that initial parse using the DOM method of calling createElement('script'), setting its src/content and adding it to the document. This is what jQuery's getScript does.
However it wouldn't do you much good because the next script, that ads1.qadabra.com is document.writeing to the page, also itself calls document.write.
You could work your way around both of these calls at the client side (ie without getad.php), by assigning your own custom function to document.write that, instead of writing to the loading page, attempts to extract the source of the script tag passed to it, and load that in a DOM-created script element.
But in general these are scripts designed to work synchronously at document load time; anything you do to try to force them to run in a way they weren't intended to is likely to be fragile and stop working when the ad network change anything.
If you want to load a third-party ad without pausing the loading of the parent document, I suggest putting it in an iframe.
I'm looking for best practices for using javascript/jQuery snippets in an asp.net project. I know that it is best to put all the scripts in a separate file rather than inline. That's good. It is easy to move these script functions to a common file (may be a couple of different ones to even out the performance of loading a single large file for small functions).
But there is some jQuery stuff that needs to happen on document.Ready on each page. How will I move this to a common .js file? I would like to avoid one script per page as it would be just too many.
For example, say Page1 has a need to manipulate a few radio buttons on load and has the following script inline. (just for illustration)
<script>
$(document).ready(function() {
//check checkboxes
if(true)
call function1();
});
</script>
Same with Page2 but for some other condition, calling different function function2.
I can move the function1 and function2 to a common .js file but how about the document ready sections. Should that stay inline? I assume so because otherwise I'm not sure how the common.js will differentiate between document.ready for different pages.
Then does it defeat the purpose of not having inline javascript? If anyone can throw some light into this, it is much appreciated.
I did some research, but probably due to incorrect keywords, so far I haven't been able to find any good information along the same lines. Unobtrusive JavaScript seems promising in the comments below.
You should specify what behaviors should exist within the HTML using data-* attributes.
You can then use a single universal piece of Javascript code to read these attributes and apply behaviors.
For example:
<div data-fancy-trick="trick-3">...</div>
In the JS file, you can write something like
$('[data-fancy-trick]'.each(function() {
var trickName = $(this).data('fancy-trick');
switch (trickName) {
...
}
});
For real-life examples of this technique, look at Bootstrap's Javascript components.
You can simply have separate js files per page and include them in relevant pages. For shared script code, have a common js file. Following your example:
common.js
var myCommonVar = {};
function myCommonFunction(...){
...
}
page1.js
$(document).ready(function() {
...
function1();
...
});
page2.js
$(document).ready(function() {
...
function2();
...
});
page1.html
...
<script src='/js/common/js'></script>
<script src='/js/page1.js'></script>
...
page2.html
...
<script src='/js/common/js'></script>
<script src='/js/page2.js'></script>
...
Consider the usage of AMD (Asynchronous Module Definiton) design pattern. Put your JavaScript code into modules and on each page use just those you really need to. For example requirejs does a great job and I've been using it with success. If you have a bigger project you can split your modules into namespaces. This approach will keep excellent code maintainability and it's reliable. You simply put the "starter" javascript file on each page and load only those required modules you need to work with per each page.
There are many ways to deal with this problem, either using a JavaScript Framework that is aiming to treat your website as a 'Webapp' (Angular and Ember among the popular), or using your own custom script that will do just that - invoking the appropriate JavaScript per loaded page.
Basically, a custom script that will be able to handle it, will have to make use of (pseudo) 'Namespaces' to separate modules/pages code sections.
Assuming you have 2 hypothetical pages, Home and Browse, Simplified code sample may look like this:
HTML:
<body data-page="Home">
Global.js:
var MyApp = {}; // global namespace
$(document).ready(function()
{
var pageName = $('body').data('page');
if (pageName && MyApp[pageName] && MyApp[pageName].Ready)
MyApp[pageName].Ready();
});
Home.js:
MyApp.Home = MyApp.Home || {}; // 'Home' namespace
MyApp.Home.Ready = function()
{
// here comes your 'Home' document.ready()
};
Browse.js:
MyApp.Browse = MyApp.Browse || {}; // 'Browse' namespace
MyApp.Browse.Ready = function()
{
// here comes your 'Browse' document.ready()
};
MyApp.Browse.AnotherUtilFunc = function()
{
// you could have the rest of your page-specific functions as well
}
Also, since you're using ASP.NET MVC, sometimes your Controller name may fit as the qualified page name, you can set it automatically in your Layout.cshtml (if you have one):
<html>
<head>
</head>
<body data-page="#ViewContext.RouteData.Values["Controller"].ToString()">
#RenderBody()
</body>
</html>
I think its not worth stuffing up everything in a single file and separating them with conditional statements, just to avoid adding a reference on the respective file.
If you have code that can be called on 2,3 or more pages, then we can opt for having them in a common file. But if its going to be called on a single page then we must write code on the respective page only. This will also increase the overhead of declaring the functions that are not going to be called on the current page
And when you are using the common js file, then you don't need to worry about the $(document).ready(); event, you can use a single ready event in the common file and separate the code by using conditional statements.
The new versions of the script manager will combine everything into one blob of a script. In theory it makes fewer round trips and things run faster. In practice you could end up with several large scripts that are nearly identical and each page needs its own blob of a script. If your making one of those never change the url website pages then this is the way to go.
I came up with these best practices when I was working with jquery on ASP.Net
Load Jquery in your master page above the first script manager. Jquery is now available on every page. The browser will only get it once and cache it.
If bandwidth is an issue use a jquery loader like googleload or MS content delivery network
Document.load is always at the bottom of the page to guarantee that everything needed is already loaded.
From my blog that I haven't updated in years...Google Load with ASP.Net
One common way to address this problem would be to have your common script include followed by a per-page script element:
<!-- In 'shoppingcart.html' -->
<script src="main.js"></script>
<script>
// Let there be a onDomReady JS object inside main.js
// that defines the document.ready logic on a per-page basis
$(document).ready(onDomReady.shoppingCart);
</script>
Great question, I have been dealing with the same thing.
Here is what I have been doing:
Have your $(document).ready() call different init functions (if they exist), where each .js file has its own init which adds event listeners and loads functions, messes with css, etc.. Each .js file is separated out into different pieces of functionality.
This way you have one document ready that calls all of your initializers. So each page would include the .js functionality it needs. This way you can separate out what is different.
ex:
ready.js:
$(document).ready(function(){
if (typeof menuNavInit == 'function'){
menuNavInit();
}
if (typeof menuNavDifferentInit == 'function'){
menuNavDifferentInit();
}
//other .js functionality
});
menuNav.js
function menuNavInit(){
$("#menu").on('click', menuNavClick)
}
function menuNavClick(){
//do something
}
menuNavDifferent.js
function menuNavDifferentInit(){
$("#menu").on('click', menuNavDifferentClick)
}
function menuNavDifferentClick(){
//do something else
}
page1.html
...
<script src='scripts/ready.js'></script>
<script src='scripts/menuNav.js'></script>
...
page2.html
...
<script src='scripts/ready.js'></script>
<script src='scripts/menuNavDifferent.js'></script>
...
The original code I have is
<script type="text/javascript" src="http://platform.linkedin.com/in.js">
api_key: qwerty
</script>
I want to make this code happen using jQuery after something has been triggered. Something like this
jQuery.getScript('http://platform.linkedin.com/in.js');
The problem is that I am not sure what the api_key part does, I've never seen the combination of a request to external lib and code between script tags. Is there a way to imitate that with jQuery? And also - what does that line do? :)
Thanks!
It's not exactly the answer to the question I originally posted, which is more abstract. But in case someone stumbles on this question in connection to linkedin, here's the way to load their framework asynchronously.
jQuery.getScript("http://platform.linkedin.com/in.js?async=true", function success() {
IN.init({
api_key: "qwerty"
});
});
I did some digging through what happens when you load the script, and found that in.js parses the contents of the tag whose src is itself and uses them to build a new script tag to append to the head.
Based on your post, it appended
<script src="https://www.linkedin.com/uas/js/userspace?v=0.0.2000-RC1.20888-1402&apiKey=qwerty&"></script>
to the head. userspace.js obviously rejected the api key, but because this is a proprietary method of loading data, I can't predict how it'll work if and when you try to turn that into an ajax call.
Update:
According to the script tag standard, "If the src has a URI value, user agents must ignore the element's contents and retrieve the script via the URI." This allows LinkedIn to get away with putting invalid Javascript inside the script tag, knowing it'll never get evaluated by the browser.
This should work correctly on jQuery mobile site.
<script type="IN/Apply" data-companyname="XXX" data-jobtitle="XXX" data-joblocation="XXX" data-email="XXX#XXX.XXX">
</script>
<script type="text/javascript">
$(function() {
if(typeof(IN)=="undefined"){
$.getScript("//platform.linkedin.com/in.js?async=true", function success() {
IN.init({api_key: "XXX"});
});
}
else{
IN.parse();
}
});
</script>