DOM: creating elements
In this slide deck,
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.
DOM: document.createElement()Definition:
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.
DOM: Element.setAttribute()Definition:
This 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 automatically converted 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-string value specified is
automatically converted into a
string.
Return value: undefined.
Exceptions:
InvalidCharacterError: The specified attribute
name
contains one or more characters
which are not valid 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 slide deck.
Code Example:
img.setAttribute('src', `${imageUrl}`);
This code was taken from the Week 10 Homework's
.md file. This
is a good example of how the value of the src attribute
can
be dynamic
too.
DOM: Node.appendChild()
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);
The above code is taken from our Note App Project 7.
Code Example:
function append(parent, el) {
parent.appendChild(el);
}
This code is taken from our Monsters Search API
Project 8
application.
In essence, the createElement() and appendChild() methods
contribute to the
extension of
the DOM Tree.
DOM: the Node InterfaceWhat 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.
DOM: HTML parsing 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.
WHATWG
(Web
Hypertext
Application Technology Working Group) best describes this
process in their HTML Living Standard:
Overview of the parsing model: a stream ofcode pointsis input into theHTML parsingprocess, and passed through atokenizationstage, followed by atree constructionstage. Theoutputis aDocument object.
DOM: tokenizationIn 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.
DOM: tokenization
DOM: code pointscode 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.
DOM: construction of the DOM TreeWhen 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.
DOM: construction of the DOM Tree
DOM: Document.createTextNode()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.
DOM: document.createTextNode() breakdownDefinition:
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.
DOM: textNode vs innerHTMLWhen 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);
DOM: when textNode is
preferable over
innerHTMLUsually 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.
DOM: .textContent vs
.innerHTMLWe'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.
DOM in action
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;
DOM: recapIn 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
Other methods (and properties) used were:
document.getElementById('idName') - method
.innerHTML - property
classList.add('className') - classList is a property,
.add() is a built-in method.
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:
load eventDid 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 element is a ul element. 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!
load event: scripts &
external
stylesheetsIf a script element 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 element, but start
downloading it only
after it has downloaded
the whole HTML document. For
long HTML documents, this may
result in a long delay.
load event: page load
performanceWhen loading a script on an HTML
page, you need
to be careful
not to damage
the page's load
performance.
Whenever the HTML parser comes
across the script element, 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.
load event: the importance of the
script positionWhen some of us first started learning
HTML, we may have been
told to place the script element
in the
head.
Giving the
parser immediate access to the
script element could result in
even longer page
load delays. It's only after the
element has been
executed will the parser
continue parsing the rest
of
the HTML.
The common solution to this problem
is to
place the script element 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.
load event: async and defer
script
attributesBoth async and defer are boolean
attributes. They
are added to
the script element
in the same way:
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 the opening 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 element in the head. They
don't do anything if you place your
script element above the
closing body tag.
load event: the async vs
defer script
tag attributedefer or async attribute in the opening
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
element 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 element
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.
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.
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.