Catch 22: Load Script if Element That Depends on Script Exists - javascript

My goal is to load javascript in the <head> only if a certain element exists in the <body>.
However, I have a problem: I am loading a Web Component, which is just a <script> hosted elsewhere and is pulled in as:
<script src="https://someurl.com/some-web-component.min.js"></script>
This web component file is huge, so I don't want to pull it in unless it is inserted into body by our Content Management System (CMS).
The constraints I am working under are:
• The <head> is shared between pages, so I need conditional logic
• I have no control over the <body> inserted by the CMS, which will potentially contain the <my-web-component> tag
• I NEED the script to load the web component, so I can't use jQuery's $(document).ready, at least I think I can't - an error will be thrown because the browser won't know the element exists
This plunker partially shows my problem, minus all the web component complexity:
https://plnkr.co/edit/GGif2RNHX1iLAvSk1nUw?utm_source=next&utm_medium=banner&utm_campaign=next&p=preview
Any way around this?

You can use DOMContentLoaded event.
The DOMContentLoaded event is fired when the initial HTML document has
been completely loaded and parsed, without waiting for stylesheets,
images, and subframes to finish loading.
In this case you can look for the Component and add the script with something like the following
document.addEventListener("DOMContentLoaded", function(event) {
if(document.querySelector('Component')){
var script = document.createElement('script');
script.src = 'https://someurl.com/some-web-component.min.js';
document.head.appendChild(script)
}
});
Probably a better approach though would be to add the script in the head with async attribute and later remove it if the component is not found.
Something like this
<script async src = "https://someurl.com/some-web-component.min.js"> </script>
<script >
document.addEventListener("DOMContentLoaded", function(event) {
if (document.querySelector('Component') == null) {
var script = document.querySelector('script[src="https://someurl.com/some-web-component.min.js"]')
document.head.removeChild(script)
}
});
</script>
More about DOM lifecycle events
More about async script loading

I am using $(document).ready and inside this method checking if element exists or not. It is working completely fine for me. Below is code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQuery Test Element Exists or Not</title>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var elem = document.querySelector('h1');
var isElemPresent = !!elem;
console.log('Is the element present: ', isElemPresent);
});
</script>
</head>
<body>
<h1>Hello Plunker!</h1>
<script>
var elem = document.querySelector('h1');
var isElemPresent = !!elem;
console.log('Oh NOW it works...: ', isElemPresent);
</script>
</body>
</html>
I am not sure where you are facing issue while using jQuery. There might be some other issue. Above approach is good enough to load script after checking if element is present.
Plunker link:
https://run.plnkr.co/preview/cjgczwlzt000knneldv5d52ea/

Related

JavaScript - Loading the entire webpage not specific div

I'm new to JavaScript and I'm sure that this is a very trivial fix.
I'm dynamically changing a div content based on which button is clicked. This example works in JSFiddle but however when I put it on my PC it simply loads the entire webpage even when I wrap the JS with $(window).load(function(){ ... })
My HTML:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script src= "http://code.jquery.com/jquery-1.10.2.min.js" type="text/javascript"></script>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<ul class="menu">
<li>About</li>
<li>Contact</li>
<li>Misc</li>
</ul>
<div id="about" class="menu-content">About</div>
<div id="contact" class="menu-content">Contact</div>
<div id="misc" class="menu-content">Misc</div>
</body>
</html>
My JS (script.js):
$(window).load(function(){
var $content = $('.menu-content');
function showContent(type) {
$('div', $content).hide();
$('div[data-menu-content='+type+']').show();
}
$('.menu').on('click', '.menu-btn', function(e) {
showContent(e.currentTarget.hash.slice(1));
e.preventDefault();
});
showContent('about');
});
$(window).load(function(){ ... })
replace by :
$(document).ready(function(){ ... })
Replace your (window).load to (document).ready
load is called when all assets are done loading, including images. ready is fired when the DOM is ready for interaction.
load()
The load event fires at the end of the
document loading process. At this
point, all of the objects in the
document are in the DOM, and all the
images and sub-frames have finished
loading.
ready()
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.
In most cases, the script can be run
as soon as the DOM hierarchy has been
fully constructed. The handler passed
to .ready() is guaranteed to be
executed after the DOM is ready, so
this is usually the best place to
attach all other event handlers and
run other jQuery code. When using
scripts that rely on the value of CSS
style properties, it's important to
reference external stylesheets or
embed style elements before
referencing the scripts.
try this
$(document).ready(function(){
var $content = $('.menu-content');
function showContent(type) {
$('div', $content).hide();
$('div[data-menu-content='+type+']').show();
}
$('.menu').on('click', '.menu-btn', function(e) {
showContent(e.currentTarget.hash.slice(1));
e.preventDefault();
});
showContent('about');
});
You can try below solution :
function showContent(type) {
$($content).hide();
$('#'+type).show();
}
When i ran your snippet in my PC, I found out that Jquery was not able to find div, based on the selectors you have specified at the time of loading.

JavaScript Events - During page is loading or before?

I'm using an event which is called after the complete site is loaded. So I use onload() for that.
Is there any way to call my function before or during the site is loaded?
I would be very grateful!
Thank You!
<html>
<head>
<title>My title</title>
<script>
var x = 2;
function timesTwo(num){
return num * 2;
}
console.log(timesTwo(x));
</script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
That way your JavaScript code is being interpreted and executed before the websites Body is being rendered. Keep in mind, that if you use that approach and are executing some JS that takes up some time, the websites display time will be delayed by same amount.
If you want to call something as early as possible, put it in a script tag at the beginning of the <head> element. However, you can't guarantee any libraries are loaded or any of the page has been loaded yet. If you want to do something as soon as possible, and are using jquery, use $(function() { yourFunctionHere() }). If you aren't using jquery, use the DOMContentLoaded event
You may listen on the 'readystate' event to do something before the 'DOMContent' event. And do not forget to put the snippet in head tag.
<html>
<head>
<script>
document.addEventListener('DOMContentLoaded', function () {
console.log('DOM content loaded');
};
document.addEventListener('readystatechange', function () {
console.log('[Ready state is]', document.readystate);
if (document.readystate != 'complete') {
console.log('You can do something here');
}
};
</script>
<body>
</body>
</html>
The output can be:
[Ready state is] interactive
You can do something here
DOM content loaded
[Ready state is] complete
Hope it helps.

In what order would the dynamical added script files be executed in this case?

I read an article here:
https://varvy.com/pagespeed/defer-loading-javascript.html
It says that I should use something like this to load javascript:
<script type="text/javascript">
function downloadJSAtOnload() {
var element = document.createElement("script");
element.src = "defer.js";
document.body.appendChild(element);
}
if (window.addEventListener)window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>
I am wondering, if I have more than one defer.js to load, in what order would it be executed?
For example:
<script type="text/javascript">
function downloadJSAtOnload() {
var element1 = document.createElement("script");
element.src = "defer1.js";
document.body.appendChild(element);
var element2 = document.createElement("script");
element.src = "defer2.js";
document.body.appendChild(element);
}
if (window.addEventListener)window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;
</script>
Will the defer1.js SURELY be executed before defer2.js?
Thanks
appendChild()synchronously appends the script node, but it loads the script asynchronously. So the execution order is undefined (it depends on network delays).
I am wondering, if I have more than one defer.js to load, in what order would it be executed?
Chaotically.
Will the defer1.js SURELY be executed before defer2.js?
No.
When you add scripts dynamically like that, there is no guarantee about the order. They'll be run as soon as they're retrieved, and so if defer2.js is retrieved more quickly than defer1.js, it'll run first.
To do them in order, you'd have to request them in order, which means you can't request defer2.js until you've received a load event on defer1.js. Which means you'll download your JavaScript more slowly, because the browser can't make the requests in parallel, which it can do if you just put the script tags at the end of the document just prior to the closing </body> tag.
You've said you're concerned about getting a less-than-perfect score from PageSpeed Insights. Don't worry. Despite what your linked article says, PageSpeed Insights is plenty smart enough to know that script tags immediately prior to the closing </body> tag do not pose any load-time problem. This page, for instance, receives 100/100 on PageSpeed Insights if you serve the JavaScript with proper caching headers:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Testing</title>
<meta name=viewport content="width=device-width, initial-scale=1">
<style>
body {
font-size: 16px;
}
</style>
</head>
<body>
<p>Testing 1 2 3.</p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="script1.js"></script>
<script src="script2.js"></script>
</body>
</html>
(Where script1.js and script2.js have a bit of jQuery code in them.)
Of course, where possible, avoid having multiple scripts (e.g., combine script1.js and script2.js above if you can; there are arguments either way about whether to include jQuery in that one combined file or use a CDN as above).
defer1.js this script will load first and then
defer2.js thi one load and so on

Why use window.onload

I have tried finding an answer to this on my own, but only found instructions on how to use onload events. I seem to be missing the point.
I've been taught that if I want something to happen when the page loads, I should use window.onload like this:
<script>
window.onload = dosomething();
function dosomething()
{
window.alert('hello');
}
</script>
But now that I am thinking on my own I wonder what the point of doing that is. Because this also produces the same result:
<script>
dosomething();
function dosomething()
{
window.alert('hello');
}
</script>
Anything I put at the top inside <script> is going to execute anyway... so what's the point of window.onload?
If you're directly running your code with dosomething();, you're delaying your browser's rendering for the time it takes your JavaScript code to run.
You can try to insert your code to the <head> of your html document:
<!DOCTYPE html>
<html>
<head>
<script>
dosomething();
function dosomething()
{
window.alert('hello');
}
</script>
</head>
<body>
Does not render before the alert is dismissed!
</body>
</html>
You'll see that the page stays blank until you dismiss the alert. So every second the browser takes to run your JavaScript code is a second that your users have to wait for the site to be rendered.
Now if you change the code to be run on body's onload, the page gets rendered before the alert is shown:
<!doctype html>
<html>
<head>
<script>
function dosomething()
{
window.alert('hello');
}
</script>
</head>
<body onload="dosomething()">
This page gets rendered before the alert!
</body>
</html>
Consider these two blocks of code:
<head>
<script>
alert(document.getElementById('foo').value);
</script>
</head>
<body>
<input id="foo" value="hello">
</body>
<head>
<script>
window.onload = function() {
alert(document.getElementById('foo').value);
}
</script>
</head>
<body>
<input id="foo" value="hello">
</body>
In the first example, we'll get an error because the element you are referencing isn't found when the script runs - and so you are trying to get value of null.
In the second example, document.getElementById() will find the element with the id foo, because window.onload will get fired only when the complete DOM has been loaded and so the element is available.
window.onload will fire once the DOM has finished loading. In your example, the DOM is not required. However, the following code will fail if the DOM has not yet loaded:
function doSomething() {
alert(document.getElementById('test').innerText);
}
// Throws: TypeError: Cannot read property 'innerText' of null
Assuming your page contains an element with id test, it will alert its text.
waiting for the onload event assures you that all of your scripts and resources are loaded
Assume you are using jquery in your page and you invoked a function that uses it directly without onload , you can't guarantee that the jquery file has been loaded, which will lead to errors and possibly ruining your whole logic
The onload event is handy to make sure the page is fully loaded before you run a script. For your example above it doesn't make sense, but if your page is still loading an item on the bottom and you try to call it then nothing will run.
I recommend using jQuery and using the ready function. This way you will ensure your page is completely loaded.
$( document ).ready(function() {
// This will only run after the whole page is loaded.
});
If you don't want to load query, just put your javascript at the bottom of the page. It's best practice, and ensures the DOM is loaded in full.
For more info on the jquery ready function go here: https://api.jquery.com/ready/

DOM & JavaScript - When is the right time to source a JavaScript file?

I'm working on a simple page that uses only <canvas> within the <body> of the page. All of the content is to be loaded through javascript. I am having trouble with using the document in my javascript and I was wondering if anyone could point me in the right direction of using <script> tags. Here is my main question:
What is the appropriate placement of <script> for a function loaded with window.onload
Here is the code I am working with:
index.html
----
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="window.js" type="text/javascript"></script>
</head>
<body>
<canvas>Canvas is not supported by your browser!</canvas>
</body>
window.js
----
Window = function(doc, win)
{
this.doc = doc;
this.win = win;
this.initialize();
}
Window.prototype =
{
initialize: function()
{
this.doc.documentElement.style.overflow = 'hidden';
this.doc.body.scroll = "no";
this.resize();
this.win.addEventListener('resize', this.resize.bind(this));
},
resize: function()
{
_canvas = this.doc.querySelector('canvas');
_canvas.width = this.win.innerWidth;
_canvas.height = this.win.innerHeight;
}
};
window.onload = new Window(document, window);
In all the tests of this script I have run, the only instance where it works is when the <script> is placed after the initial <body> tag. When I place the <script> in the <head> it gives me an error saying:
Uncaught TypeError: Cannot set property 'value' of null
Is it not a possibility for the sake of a clean looking document to have <script> be in the <head>?
Any clarification or direction on what the proper practice is would be greatly appreciated!
Script tags should go at the bottom of the page typically. This ensures all content has loaded and is ready for interaction...
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<canvas>Canvas is not supported by your browser!</canvas>
<script src="window.js" type="text/javascript"></script>
</body>
</html>
If you don't put the script in after the element, as far as your script is concerned, that element does not exist. It needs to be in the bottom, or at least after the canvas element.
In your case, it should be in the bottom, after the <canvas> element.
It really doesn't matter where your JS files are loaded. Your problem is that the JS files could possibly load before your DOM is fully drawn. I've had pages where JS at the bottom of the page was executing before the browser was done loading the middle. That's why every JS framework contains something to check if the DOM is ready or not. in jQuery you would use ready
$(document).ready(function() { alert('My DOM is loaded!'); });
Outside of jQuery, you could use DOMContentLoaded. Put this at the bottom of your window.js file and you can load it in your header without issue.
document.addEventListener("DOMContentLoaded", function(event) {
new Window(document, window);
});

Categories

Resources