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

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);
});

Related

Loading widget on web page

I'm trying to make a loadable widget that would call an api when a button is clicked.
Widget page itself
widget.html
<html>
<head>
<title>Widget</title>
</head>
<body>
<button id="test"></button>
<script>
window.onload = init;
function clicked() {
// some logic
}
function init() {
document.getElementById('test').addEventListener('click', clicked, false);
}
export {clicked}
</script>
</body>
</html>
The page it embeds on
index.html
<!DOCTYPE HTML>
<html>
<head>
<title>Getting started</title>
</head>
<body>
<div>
<div id="widget_box"></div>
<script src="http://localhost:8080/js/widget.js" type="text/javascript"></script>
<script type="text/javascript">wdgt.init('widget_box');</script>
</div>
</body>
</html>
js code that loads the widget
widget.js
var wdgt= {
idBox: 'wdgt',
url_widget: 'http://localhost:8080/pages/widgets/widget.html',
url_style: 'http://localhost:8080/css/widget.css',
init: function (id) {
console.log("Begin Widget initialization");
if (!id) {
id = this.idBox;
}
if (document.getElementById(id)) {
this.addStyle();
try {
var XHR = ("onload" in new XMLHttpRequest()) ? XMLHttpRequest : XDomainRequest;
var xhr = new XHR();
xhr.open('GET', this.url_widget, true);
xhr.onload = function () {
if (this.response) {
document.getElementById(id).innerHTML = this.response;
}
}
xhr.onerror = function () {
console.log('onerror ' + this.status);
}
xhr.send();
} catch (ignore) {
}
} else {
console.log('The specified block id="' + id + '" is missing');
}
},
addStyle: function () {
style = document.createElement('link');
style.rel = 'stylesheet';
style.type = 'text/css';
style.href = this.url_style;
document.head.appendChild(style);
}
}
If I open the widget in a separate window, the code attached to the button works. But when I try to embed it, nothing happens.
Moreover, in this script, document.getElementById() returns null when trying to find the button.
The back is a simple Spring application that returns index.html
TL;DR: It's not supposed to work. You should use iframe element to fetch and render external pages.
Your script widget.js is trying to render the contents of fetched widget.html into a <div> element.
The browser will to do the best, though it won't be able to create another DOM tree inside of <div>. Because when you feed a whole HTML page to innerHTML, you basically ask the browser to render a document inside a document. This is not allowed.
Still it will render the contents of you document's body. So you'll be able to see the button. But it won't execute any <script> tags. It's a safety measure to prevent XSS attacks. A couple of dirty ways to execute your code do exist, but I won't recommend them.
So what should you do to add an external widget into a page? The answer is neat: meet the <iframe>! This dude is created just for those things. For you case, you should rewrite index.html this way:
<!DOCTYPE HTML>
<html>
<head>
<title>Getting started</title>
</head>
<body>
<iframe src="http://localhost:8080/pages/widgets/widget.html" width="800" height="600"></iframe>
</body>
</html>
With iframes, you don't need to manually fetch your widget HTML. The browser will download and render it inside your iframe. Like a document inside a document with all the styles, embedded resources and scripts.
Please note, that when you embed something using iframe, security limitations apply. You won't be able to directly interact with your main page from inside the iframe and vice versa. It will be like a separate window inside you document. And it will have additional constraints. Please address to the docs.

Run javascript after navigating to a page

I have the following javascript in some pug file:
doctype html
html
//- this is not firing for some reason
head
script(type="text/javascript").
document.addEventListener('DOMContentLoaded', function() {
console.log('ready!')
}, false);
the script only runs when I refresh the page, not when I navigate to the page the script do not fire. I am looking for an event like onNavigate, however I do not see it in the list here: https://www.w3schools.com/jsref/dom_obj_event.asp or here: https://developer.mozilla.org/en-US/docs/Web/Events
Your code should work fine(if you code is written in this way). Don't see a problem.
<!doctype html>
<html>
<body>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
console.log('ready!')
}, false);
</script>
</body>
</html>
or
<!doctype html>
<html>
<body>
<script type="text/javascript">
window.onload = function() {
console.log('ready!')
};
</script>
</body>
</html>
Ya know the body OnLoad event occurs after any css and js loads - including external refs.
All you really need is:
<body onload="do_this()">
and then in the script element
function do_this(){
// do sh1t
}

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

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/

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.

Javascript: Can't get element using getElementById [duplicate]

This question already has answers here:
Why does jQuery or a DOM method such as getElementById not find the element?
(6 answers)
Closed 6 years ago.
Ok. I need fresh eyes because I'm still on this s***d problem for one hour!
Here is my simple HTML code (testssio.html) that include javascript script:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var ssio = document.getElementById('ssio');
ssio.html = "it finally works!";
</script>
</head>
<body>
<div id="ssio"></div>
</body>
</html>
But it doesn't work! Using the debugger, I get:
Uncaught TypeError: Cannot set property 'html' of null /testssio/:6
Does anyone get it? I know it's not the correct place to look for debugging help, but I'll be crazy if I don't get it! So please, any help?
Tahnks in advance.
The reason for this is that scripts in the head load before the page is rendered. This means your content is not yet rendered and therefore not a part of document.
If you want to see this work, try moving your script below the element renders, like this:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="ssio"></div>
<script type="text/javascript">
var ssio = document.getElementById('ssio');
ssio.innerHTML = "it finally works!";
</script>
</body>
</html>
A more standardized way of doing this is with events. Many people use jQuery but it can be done with plain js. This would mean changing your script like this:
<script type="text/javascript">
function WinLoad() {
var ssio = document.getElementById('ssio');
ssio.innerHTML = "It finally works!";
}
window.onload = WinLoad;
</script>
This way you can still leave it in the <head>.
Also, using .html is from jQuery. It is generally used as .html(content). If you want to use the plain javascript version use .innerHTML = content.
I mention jQuery so much because it is a highly used API. This quote is from their site:
jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript.
Your code is running too early before the DOM is loaded and thus document.getElementById() doesn't find the element in the document yet.
You can either move your script tag down to right before the </body> tag or you can wait for the DOM to load before running your code with either the window onload event or a DOMReady event.
There are two errors here. First, you need to put the SCRIPT tag after the element. Second, it's not .html, but .innerHTML. So here is the corrected code:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="ssio"></div>
<script type="text/javascript">
var ssio = document.getElementById('ssio');
ssio.innerHTML = "it finally works!";
</script>
</body>
</html>
you can use something like this
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
document.onload= function(){
var ssio = document.getElementById('ssio');
ssio.html = "it finally works!";
}
</script>
</head>
<body>
<div id="ssio"></div>

Categories

Resources