I'm using HTML templates (Jekyll) and occasionally want to include scripts in the content of the template. For example, my page template looks like this
<body>
{{ content }}
<script src="some_library.js"></script>
<script>
// general setup stuff
</script>
</body>
Where {{ content }} can sometimes include
<script>
// page-specific code relying on some_library.js and general page setup stuff
</script>
I could just move the template scripts into the head of template, but I've read many times how important it is to put <script> at the end of the document. Can I reliably ensure that the page-specific code runs last even though it appears first?
I'm using jQuery, but if I include jQuery in the template, then it won't be available in the content script. I tried adding a $(window).load function around the content script, but I get $ is not defined.
You could directly work with the window load event instead of $(window).load - no need for jQuery!
The simplest way:
window.onload= yourFunction;
A cleaner way would be to rely on the attachEvent/addEventListener methods:
window.addEventListener ?
window.addEventListener("load",yourFunction,false) :
window.attachEvent && window.attachEvent("onload",yourFunction);
(attachEvent is for older IE versions)
Use jQuery's .ready() function.
From the documentation:
While JavaScript provides the load event for executing code when a
page is rendered, this event does not get triggered until all assets
such as images have been completely received.
Also from the docs:
$(function() {
// Handler for .ready() called.
});
Related
I want to run this javascript on my homepage when it loads:
$(".mm-page").css({"position":"inherit"});
I added this at the bottom of my home.html.erb:
<% content_for(:after_js) do %>
<script type="text/javascript">
console.log("before");
$(".mm-page").css({"position":"inherit"});
console.log("after");
</script>
<% end %>
Both console.log appear in the console, but the jquery has no effect. If I manually run the jquery line in the console, it works as expected.
How should I proceed to fix this ?
Since you want to make it work on page when it load you should wrap it inside ready handler.The ready() method specifies what happens when a ready event occurs.
Two syntaxes can be used:
$(document).ready(function(){
$(".mm-page").css({"position":"inherit"});
});
OR
$(function() {
$(".mm-page").css({"position":"inherit"});
});
Also be sure that the element .mm-page exists in the moment you're using it with the jQuery selector.
To ensure that all of the DOM elements it operates on exist, put the script tag at the very bottom of the HTML, just prior to the closing </body> tag. All of the elements defined by the HTML above it will then be available for use. This also ensures that the browser can show the user the page prior to downloading any external script files you reference. E.g.:
<!doctype html>
<html>
<!-- ...your page here... -->
<script src="jquery.js"></script>
<script>
console.log("before");
$(".mm-page").css({"position":"inherit"});
console.log("after");
</script>
</body>
</html>
You'll need to translate that to whatever rendering engine you're using, but you get the idea. The end result going to the browser should look like that.
Alternately, you can use jQuery's ready callback, but with the script tags in the correct location, it's unnecessary.
Side note: The default type is JavaScript, there's no need to specify it on the script tag.
console.log appear in the console
Because your DOM has finished loading and it is now ready to use.
That's why you are getting jQuery effect.
In your current script,it is not waiting to finish DOM loading and directly operating on it which is not available as it is not finished loading.
Eiher write script it in
$( document ).ready(function() {
// Handler for .ready() called.
});
OR :
$(function() {
// Handler for .ready() called.
});
I tried to load doubleclick.net ad tags on document.ready, but the ads don't show up.
HTML
<script language="JavaScript" type="text/javascript" data-ad-src="http://ad.ch.doubleclick.net/adj/swisswebcams/;lng=de;kw=home;tile=3;dcopt=ist;sz=160x600;ord=1874680027?"></script>
JavaScript (requires jQuery)
$(document).ready(function(){
$('script[data-ad-src]').each(function(){
this.src = $(this).attr('data-ad-src');
$(this).removeAttr('data-ad-src');
});
});
The script shows up correct in the generated source code, but it doesn't load the ads anymore. Does the script require the document.ready event? Is there maybe a way to load this script just before document.ready - or to trigger document.ready again?
PS: I prefer to use the "sync" tags over the "async" tags, because "async" is creating an iFrame which then is not flexible in width/height anymore when showing 3rd party networks dynamically.
Try this
<script>
var wr = document.write, dchtml=[];
document.write=function(str) {
// you may want to catch '<script' and add the src to the head when needed
dchtml.push(str);
}
</script>
<script language="JavaScript" type="text/javascript" data-ad-src="http://ad.ch.doubleclick.net/adj/swisswebcams/;lng=de;kw=home;tile=3;dcopt=ist;sz=160x600;ord=1874680027?"></script>
<script>
$(function() { // assuming jQuery is loaded before this block
$("#whereIWantMyAds").html(dchtml.join("\n"));
});
<script>
Check your JavaScript errors. Most likely this is a problem with asynchronous downloading of the script, in fact: I'm sure. This is in the script from doubleclick (downloaded from the link you provided:
document.write('\x3cdiv...
a document.write doesn't work since document.ready already closed the document DOM. You specifically need to add the code to an element in your DOM, which can't be done with document.write. In order to make this work you have to either contact doubleclick and make them change every document.write to something that attaches the code to an element in your page, or asynchronously load the code (including the script) in an iframe.
I have this script referenced inside my main.master file:
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.0.min.js" type="text/javascript"></script>
and inside of my Web User Control I have this jquery but it isnt working, i cant really see where there would be a problem. My code works just fine inside of jsfiddle:
<script type="text/javascript">
$(".package-container").click(function () {
$(this).closest('.radio-group-row').find('.package-title').removeClass('highlight');
$(this).find('input:radio').prop('checked', true);
$(this).find('.package-title').addClass('highlight');
});
</script>
EDIT
My jquery is referenced near the bottom of my master page above the closing body tag.
Make sure your jQuery include is placed early on the page (HEAD element) and either place your code at the end of the body element or wrap it in a DOM ready handler like this:
<script type="text/javascript">
$(function(){
$(".package-container").click(function () {
$(this).closest('.radio-group-row').find('.package-title').removeClass('highlight');
$(this).find('input:radio').prop('checked', true);
$(this).find('.package-title').addClass('highlight');
});
});
</script>
$(function(){YOUR CODE HERE}); is a shortcut for $(document).ready(function(){YOUR CODE HERE});
The advantage of using DOM ready handlers, is that you can place the jQuery code anywhere (including injection by child views/controls).
Update:
If you also need to locally scope your $ variable, I suggest using this rather nice shortcut DOM ready handler. It passes the jQuery instance as a first parameter to the DOM ready function you provide:
jQuery(function($){
// Your code placed here can use $ without any worry about other packages!
});
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.
I am trying to understand how to use jQuery when it is loaded using Google CDN's google.load method.
Putting the init code in the setOnLoadCallback function works great but defining functions for event handlers in the markup doesn't seem to work. In the example below, the P button works as expected but the Div button does nothing.
Yes, in this example, I can put the div's onclick handler also in the setOnLoadCallback function but does that mean that all jQuery code has to be there?
Help? Thanks
<p id="p">Content</p><button type="button" id="btn1">P</button>
<div id="div">Div</div><button type="button" id="btn2" onclick="btn2()">Div</button>
<script src="http://www.google.com/jsapi"></script>
<script>
function btn2() {
$("#div").toggle("slow");
}
google.load("jquery", "1.3.2");
google.setOnLoadCallback(function() {
$("#btn1").click(function () {
$("p").toggle("slow");
});
});
</script>
Put your Google jsapi script call and google.load at the top of <head> in your document. When run, it will just output
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"
type="text/javascript"></script>
where google.load was.
Then put all your jQuery code inside:
$(function() {
// all your jQuery code here
});
which is shorthand for $(document).ready(function(){ });
One of the key points of JQ is to be unobtrusive thus you shouldnt be using <element onclick="..."></element>. You should always be using $(selector).click(). Furthermore you generally want to have this consolidated in a single $(document).ready();, or in exeternal scripts.
Yes, you need to have all your jQuery code inside setOnLoadCallback if you want to load jQuery this way. Until that event fires, there is no guarantee that the jQuery script has loaded, and thus any jQuery code outside that function may be invalid. If you want to define jQuery code outside of that event, you can do it by loading jQuery from Google's CDN URL instead of dynamically by google.load().
The url for jQuery from Google's CDN looks like this:
http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
You can just include it in the head of the document as an external script resource as usual. You still get the benefits of the Google CDN because most browsers will have a cached copy of jQuery at that location (assuming they have visited another site that used the Google CDN).