This post has 4360 words.
This post has 27475 characters.
This post reading time is approximately 19 minute(s)
In this course
, we are going to see what it means to "create elements"
on the web page
, thereby directly manipulating the DOM
using the document.createElement()
method.
Creates an HTML
element on the page specified by tagName
, or an HTMLUnknownElement
if tagName
is not recognized. Make it a habit, however, of creating recognized elements
only!
Syntax:
const element = document.createElement(nodeName);
Parameters:
nodeName (type
String): Required. The name
of the element
you want to create.
Return value: a new Element
.
Code Example:
function createNode(element) {
return document.createElement(element);
}
The above code
is a helper function
taken from the Monsters API App
.
You may have noticed that there are no quotes around the element
argument passed in to document.createElement()
. There ARE quotes around the value of element
when the createNode()
function is called:
let li = createNode('li');
You can also remove elements
from the DOM
with the .remove()
method.
To learn more, please visit ChildNode.remove() on MDN.
Definition:
his method sets the value
of an attribute
on the specified element. If the attribute
already exists, the value
is updated. Otherwise, a new attribute
is added with the specified name
and value
.
Syntax:
Element.setAttribute(name, value);
Parameters:
name: A DOMString
specifying the name
of the attribute
whose value is to be set. The attribute name
is automaticallyconverted to all lower-case when setAttribute()
is called on an HTML element
residing in an HTML
document.
value: A DOMString
containing the value
to assign to the attribute
. Any non-stringvalue
specified is automatically converted into a string
.
Return value: undefined
.
Exceptions:
InvalidCharacterError: The specifiedattribute name
contains one or more characters which are notvalid in attribute names
.
You can also get attributes
with the .getAttribute()
method and remove attributes
with the .removeAttribute()
method.
To learn more, please refer to the Related Resources slide
at the end of this post
.
Code Example:
img.setAttribute('src', `${imageUrl}`);
This is a good example of how the value
of the src attribute
can be dynamic too.
Node.appendChild()
is a method of the Node interface
.
The Node.appendChild()
method adds a node
to the end of the list of children of a specified parent node
. If the given child
is a reference to an existing node
in the document, appendChild()
moves it from its current position to the new position.
This means that a node
can’t be in two points of the document simultaneously. So if the node
already has a parent, the node
is first removed, then appended at the new position.
Syntax:
element.appendChild(aChild)
Parameters:
aChild:
The node
to append to the given parent node
(commonly an element
).
Return value:
The returned value is the appended child (aChild).
Code Example:
document.body.appendChild(downloadLink.download);
Code Example:
function append(parent, el) {
parent.appendChild(el);
}
This code
is taken from our Monsters Search API Project 8 application
associated with this course.
In essence, the createElement()
and appendChild()
methods contribute to the extension of the DOM Tree
.
What is the Node interface
? It is an interface
that is implemented by a large number of objects
, including document
, and element
, for example.
A node
, then, in the context of the DOM
, means any object that implements the Node interface
. Usually it is an element object
representing an HTML element
.
In the slide deck An Introduction to JavaScript and the DOM, I discuss the HTML Parser
, which is implemented by the browser, and is responsible for the encoding
, pre-parsing
, tokenization
, and tree formation
of the DOM
.
The WHATWG
(Web Hypertext Application Technology Working Group) best describes this process in their HTML Living Standard
:
Overview of the parsing model: a stream of
code points
is input into theHTML parsing
process, and passed through atokenization
stage, followed by atree construction
stage. Theoutput
is aDocument object
.
In Introduction to JavaScript and the DOM, I discuss tokenization
.
Tokenization
refers to the process
by which a stream of code points
are transformed from HTML markup
(what you create in the Code Editor) into individual tokens
such as a “begin tag”, “opening tag”, “end tag”, etc.
code point: refers to the numbers
assigned to characters
that are needed for a specific purpose (i.e., HTML text
), and are grouped into a specific character set
(also called a repertoire
). You may also know character set
as "charset"
. The associated html tag
is called meta charset
. The character set
used in html
pages is "utf-8"
.
To display an HTML
page correctly, a web browser
must know which character set
to use. That’s why the meta charset
tag is so important.
The default character set
for HTML5
is UTF-8.
UTF-8 (Unicode) covers almost all of the characters
and symbols
in the world.
In essence, words
and sentences
in text are created from such characters
associated with specific code points
. Generally, however, the default these days is UTF-8
.
To learn more, please refer to the Related Resources slide
at the end of this slide deck
.
When a token
is produced, it must immediately be handled by the tree construction
stage.
The input to the tree construction
stage is a sequence of tokens
from the tokenization
stage.
The tree construction
stage is associated with a DOM Document object
when a parser
is created.
The output
of this stage consists of dynamically modifying
or extending
that Document’s DOM Tree
.
In essence, the DOM
represents HTML
as a tree structure of tags
.
All viewable HTML text
in a web page
(except text
in form elements
or custom embedded objects
) is in text nodes
.
Web pages consist of a number of different types
of nodes
. Some have child nodes
and some don’t. To learn more about the different node types
, please visit Node.nodeType on MDN.
Definition:
Creates a Text Node
with the specified text
. HTML elements
often consist of both an element node
and a text node
. Let’s say you want to create a header element
that contained text
. You would have to create both an h1 element
and a text node.
Syntax:
document.createTextNode(text);
Parameters:
text (required
): of type
String. It is the text of the Text Node
.
Return value: A Text Node
object with the created Text Node
.
As we went over earlier, first we would use the .createElement()
method to create an Element Node
using the specified element name
.
After creating the Element Node
, we would create a Text Node
with the specified text
for the Element Node
. In this case
, we would create a Text Node
for the h1 Element Node
we just created using document.createTextNode()
.
Finally, after creating the text
node for the h1 Element Node
, we would use the Element.appendChild()
method (or the Element.insertBefore()
method) to append the Node Element
to a Parent Element
.
Example Code:
const para = document.createElement("p");
const paraTextNode = document.createTextNode(`I was working in the lab, late one night
When my eyes beheld an eerie sight
For my monster from his slab, began to rise
And suddenly to my surprise...`);
para.appendChild(paraTextNode);
document.body.appendChild(para);
In more modern browsers
, you could achieve the same using either the .innerHTML
property or the .textContent
property.
When you set the .innerHTML
property of an Element Node
, i.e., li
, it creates the appropriate nodes
and makes them child nodes
of the element that you set the .innerHTML
property on. If there is text
in the .innerHTML
you set, then text nodes
will be created to hold that text
.
Code Example:
const wrapperContainer = document.querySelector(".wrapper-container");
const newSection = document.createElement("section");
// append newSection to wrapperContainer
wrapperContainer.appendChild(newSection);
// create p tag
const newPara = document.createElement("p");
newSection.appendChild(newPara);
const newContent = document.createTextNode(
"I just created my first HTML elements!"
);
newPara.appendChild(newContent);
Usually one would not want to manipulate text nodes
directly using createTextNode()
, i.e., since the same can be achieved using .innerHTML
.
On the other hand, you may want to display some text
without the security risks
that it might contain other markup that the browser
would parse and interpret if you used .innerHTML
.
So, you create a Text Node
and set the value
of its text
, and the browser
won’t interpret any HTML
in it.
Modern browsers
can also use the .textContent
property to solve the same problem as well.
To learn more, please visit What Is A Text Node, Its Uses? //document.createTextNode() on stackoverflow.
We’ve already seen .innerHTML
in action, and we have seen and used examples of .textContent
along the way. They basically do the same thing – replace what is between the opening and closing tag
of an element
.
.textContent
only does that with text content
.
On the other hand, .innerHTML
takes both text
and markup
into consideration.
function addElement() {
document.body.setAttribute("class", "Site");
document.body.setAttribute("id", "Site");
// create the outer container element
const outerContainer = document.createElement("div");
outerContainer.setAttribute("class", "Site-content");
document.body.appendChild(outerContainer);
// create wrapper container that contains all the other elements
// all tags bound in the same way to wrapperContainer either directly
// or through parent.
const wrapperContainer = document.createElement("div");
// append wrapperContainer to body
document.body.appendChild(wrapperContainer);
// create wrapperContainer class
wrapperContainer.classList.add("wrapper-container");
// create app title
const manipulateDOM = document.createElement("h1");
// set id attribute
manipulateDOM.setAttribute("id", "manipulate-dom");
// set manipulateDOM innerHTML
manipulateDOM.innerHTML = `Welcome to my DOM manipulation site!`;
// append manipulateDOM to wrapperContainer
wrapperContainer.appendChild(manipulateDOM);
// create new section element
const newSection = document.createElement("section");
// append newSection to wrapperContainer
wrapperContainer.appendChild(newSection);
// create p tag
const newPara = document.createElement("p");
newSection.appendChild(newPara);
const newContent = document.createTextNode(
"I just created my first HTML elements!"
);
newPara.appendChild(newContent);
// add new elements and their content to the DOM
const bodySection = document.getElementById("Site");
}
document.body.onload = addElement;
In the previous slide
, we created some new elements
and manipulated the DOM
with the following methods:
document.createElement('tagName')
document.setAttribute('attrName', 'value')
document.appendChild(childIdentifierName)
document.createTextNode('Some text')
document.body.onload
Live example of removing/adding attributes can be found here on the slide deck related to the subject of this course hosted on Github gh-pages: Removing/Adding Attributes.
JavaScript:
function toggleSrc() {
let image = document.getElementById('image');
const hiddenCan = 'Hidden_Can.png';
const visibleCan = 'Visible_Can.png';
if (image.getAttribute('src') === hiddenCan) {
image.setAttribute("src", visibleCan);
} else {
image.setAttribute("src", hiddenCan);
}
}
const imgBtn = document.getElementById('imageBtn');
imgBtn.addEventListener('click', toggleSrc);
HTML:
<div id="wrapper">
<img src="Hidden_Can.png" id="image">
<button type="button" id="imageBtn">change me!</button>
</div>
Did you ever notice how sometimes a web page takes such a long time to load
, or the page seems to have to be refreshed a number
of times before any of the content
comes into view?
This is especially the case when there is a lot of styling
involved, images
to load, and even APIs
to fetch, which might be populating your images
as well as data content
within your document
.
The reason why I bring this up in this particular slide deck
, is because in Project 8, the Monsters API App, is all about creating elements
. The only thing we already have in index.html
inside of the body
tag is a ul
tag. Everything else is created
with the built-in method createElement()
, set on the document
object.
So what does creating elements
have to do with the load
event? What if a page is loaded and the elements
which are being created with the createElement()
method haven’t been created yet? Nothing would render to the page
!
If a script
tag comes after an external stylesheet
, then that script
must wait until the stylesheet
loads.
The reason why this happens is because the script
may want to get style dependent
properties of elements.
Example:
const monstersBtn = document.querySelector('.monsters-btn');
monstersBtn.addEventListener('click', fetchMonsters);
The most common workaround for this is to place the script
at the bottom of the index.html
, right before the closing body
tag. However, that does not completely take care of the issue. What if the page is very long and/or has many elements
to load
? In cases like that, the browser
may pick up on the script tag
but start downloading
it only after it has downloaded
the whole HTML
document. For long HTML
documents, this may result in a long delay.
When loading a script
on an HTML
page, you need to be careful not to damage the page’s load performance
.
<script src="main.js"></script>
Whenever the HTML
parser comes across the script
tag, a request
is made to fetch the script
, and the script
is executed.
Once the process is complete, the parsing
can continue, and the rest of the HTML
analyzed.
If the script
takes longer to load
than expected, the visitor will probably see a blank page until the script
is completely executed
.
Creating elements and the load
event: the importance of the script position
When some of us first started learning HTML
, we may have been told to place the script tag
in the head
. Giving the parser
immediate access
to the script tag
could result in even longer page load
delays. It’s only after the tag
has been executed will the parser
continue parsing
the rest of the HTML
.
The common solution
to this problem is to place the script tag
at the bottom of the page, right above the closing body tag
.
This is definitely a great improvement
, as the script
is then loaded and executed after the page is already parsed and loaded.
This is the best way to go if you need to support
older browsers
that do not support
the relatively recent async
and defer
attributes.
Both async
and defer
are boolean
attributes. They are added to the script
tag in the same way:
<script async="" src="main.js"></script>
<script defer="" src="main.js"></script>
Please note that in actuality, async
, and defer
, as shown in the slide deck markup itself, does not have an explicitly visible value
. It either is true
or false
. Please ignore the =""
.
If one includes both in a script tag
, async
takes precedence in modern browsers
, and defer
takes precedence in older browsers
.
Using either of these attributes
only makes sense if you are placing the script tag
in the head. They don’t do anything if you place your script tag
above the closing body tag
.
No defer
or async
attribute in the script tag
right above the closing body tag
:
The parsing
takes place without interruption, and only when it is finished is the script
loaded and executed. This means that the page
is viewable by the user
way before it would have been if the script tag
had been placed in the head
.
async script
attribute in the head
:
The script
is fetched asynchronously
, and when the script
is ready for execution, the HTML
parsing is paused, allowing for execution of the script
, and afterwards, the HTML
parsing is continued.
defer script
attribute in the head
:
The script
is fetched asynchronously
, and it’s executed only after the HTML
parsing is done. Parsing is completed the same way as with the script tag
right above the closing body tag
, but the process is much faster, because the script
has been downloaded in parallel
with the HTML
parsing. This is the best way to go, and what I ended up doing in the Monsters API App.
async
blocks parsing
:
async
blocks parsing
of the page. defer
does not.
blocking rendering
:
Neither async
nor defer
guarantee that rendering will not be blocked. It is up to you to make sure that it is not. i.e., making sure that your scripts
run after the load
event.
keeping scripts
in order:
async
ignores the order of scripts
. scripts
with the async
attribute are executed as they become available. This can be a problem when order matters. And oftentimes, it does! scripts
with the defer
attribute are executed after parsing has been completed, and in the order
in which they are defined in the markup
.
To learn more about async
vs defer
, please visit the post entitled Efficiently load JavaScript with defer and async by Flavio Copes.
Document.createElement(): MDN
HTMLElement: MDN
HTMLUnknownElement: MDN
JavaScript: How can I check for “HTMLUnknownElement”? : stackoverflow
HTML DOM createElement() Method: W3Schools
Element.setAttribute(): MDN
JavaScript appendChild: JavaScript Tutorial
Modifying the document: JavaScript.info
What Is A Text Node, Its Uses? //document.createTextNode(): stackoverflow
HTML DOM createTextNode() Method: W3Schools
JavaScript onload: JavaScript Tutorial
Script Tag – async & defer: stackoverflow
Difference between document.addEventListener and window.addEventListener?: stackoverflow
Page: DOMContentLoaded, load, beforeunload, unload: javascript.info
Scripts: async, defer: javascript.info
Efficiently load JavaScript with defer and async: Flavio Copes
What is a node in Javascript?: stackoverflow
An Intro To JavaScript And The DOM: interglobalmedia Github gh-pages
Node: MDN
12.2.1 Overview of the parsing model: HTML: The Living Standard (Web Hypertext Application Technology Working Group)
Character encodings for beginners: W3C
HTML Encoding (Character Sets): W3Schools
DOM tree: javascript.info
DOM Tree construction: WHATWG