Reminder: CP3 due today
HW3 notes:
Follow up from Friday ( buttonTest.js )
Reintroducing the keyword this
Modifying the DOM tree
Selecting multiple DOM objects
Caveat: read Piazza post 358
More practice with...
<em>
,
<strong>
and <blockquote>
,
<section>
etc, over <i>
,
<b>
, <div>
this
this
All JavaScript code actually runs inside of an object
The this
keyword refers to the current object
By default, code runs in the global window
object
so...
this === window
window
this.fieldName // access field
this.fieldName = value; // modify field
this.functionName(parameters); // call method
JS
this
See thisexample.html and thisexample.js
<select>
,
<option>
HTML
output
Option element represents each choice
select
optional attributes: disabled
, multiple
, size
option
optional attributes: disabled
, selected
, label
, value
this
with dropdownsBest to see example code in thisexample.js
HTML
window.onload = function() {
$("favorite-character").onchange = function() {
// Accessing the value from the this object
// which is the drop down favorite-character
displayFavoriteCharacter(this.value);
}
};
function displayFavoriteCharacter(value) {
$("favorite-character-output").innerHTML =
"Your favorite character is: " + value;
}
JS
The elements of a page are nested into a tree-like structure of objects
We already know how to get an element of of the DOM if the html element is
tagged with an id
We also know we can add things to the tree using innerHTML
hacking
but that we should only add bare text (see next slide)
The DOM also has
innerHTML
hacking is badWhy not just code this way?
document.getElementById("add").innerHTML = "<p>A result!</p>";
JS
Bad code quality (maintainability) on many levels
// Substitutes all children
$("add").innerHTML = "<p>A result!</p>";
// adds a node to the front of the list of children.
$("add").innerHTML = "<p>A result!</p>" + $("result").innerHTML;
// adds a node to the end of the list of children
$("add").innerHTML += "<p>A result!</p>";
JS
Name | Description |
---|---|
document.createElement("tag") | creates and returns a new empty DOM node representing an element of that type |
document.createTextNode("text") | creates and returns a text node containing given text |
// create a new <h2> node
let newHeading = document.createElement("h2");
newHeading.innerHTML = "This is a heading";
newHeading.style.color = "green";
JS
NOTE: Merely creating an element does not add it to the page
You must add the new element as a child of an existing element on the page...
Every DOM element object has these methods:
Name | Description |
---|---|
appendChild(node) | places the given node at end of this node's child list |
insertBefore(new, old) | places the given node in this node's child list just before old child |
removeChild(node) | removes the given node from this node's child list |
replaceChild(new, old) | replaces given child with new nodes |
let p = document.createElement("p");
p.innerHTML = "A result!";
document.getElementById("the-place").appendChild(p);
JS
How would we do each of the following in JavaScript code? Each involves modifying each one of a group of elements...
ul
list with id
of 'tas' to have a gray background.
from pixabay
Methods in document and other DOM objects:
Name | Description |
---|---|
getElementsByTagName(tag) | returns array of descendants with the given tag, such as "div" |
getElementsByName(name) | returns array of descendants with the specified name (mostly useful for accessing form controls) |
querySelector(selector) | returns the first element that would be matched by the given CSS selector string |
querySelectorAll(selector) | returns an array of all elements that would be matched by the given CSS selector string |
Use querySelectorAll
to highlight all paragraphs in the document:
<body>
<p>This is the first paragraph</p>
<p>This is the second paragraph</p>
<p>You get the idea...</p>
</body>
HTML
// get all DOM objects that are <p>
let allParas = document.querySelectorAll("p");
for (let i = 0; i < allParas.length; i++) {
allParas[i].style.backgroundColor = "yellow";
}
JS
This is the first paragraph
This is the second paragraph
You get the idea...
output
Highlight all paragraphs inside of the section with ID 'address':
<p>This won't be highlighted!</p>
<div id="address">
<p>1234 Street</p>
<p>Seattle, WA</p>
</div>
HTML
let addrParas = document.querySelectorAll("#address p");
for (let i = 0; i < addrParas.length; i++) {
addrParas[i].style.backgroundColor = "yellow";
}
JS
This won't be highlighted!
1234 Street
Seattle, WA
output
querySelectorAll
issues
Many students forget to write .
or #
in front of a class or
id
// get all buttons with a class of "control"
let gameButtons = document.querySelectorAll("control");
let gameButtons = document.querySelectorAll(".control");
JS
querySelectorAll
returns an array, not just a single element;
you must loop over the results
// set all buttons with a class of "control" to have red text
document.querySelectorAll(".gamebutton").style.color = "red";
let gameButtons = document.querySelectorAll(".gamebutton");
for (let i = 0; i < gameButtons.length; i++) {
gameButtons[i].style.color = "red";
}
JS
Q: Can I still select a group of elements using querySelectorAll
even if
my CSS file doesn't have any style rule for that same group?
(A: Yes!)
Use querySelector
to highlight the first paragraph in the document:
<body>
<p>This is the first paragraph</p>
<p>This is the second paragraph</p>
<p>You get the idea...</p>
</body>
HTML
// get all DOM objects that are <p>
let para = document.querySelector("p");
allParas.style.backgroundColor = "yellow";
JS
This is the first paragraph
This is the second paragraph
You get the idea...
output
The .style
property of a DOM object lets you set any
CSS style for an element
button { font-size: 16pt; }
CSS
HTML
window.onload = function() {
document.getElementById("clickme").onclick = biggerFont;
};
function biggerFont() {
let button = document.getElementById("clickme");
let size = parseInt(button.style.fontSize);
button.style.fontSize = (size + 4) + "pt"; // notice adding the units!
}
JS
output
A catch: you can only use this to read styles set
in the html/css files or with the DOM .style
.
You cannot read dynamic css properties from this.
Be careful to add the units to numerical values like pt
,
px
, vw
, etc
getComputedStyle
method of global window
object accesses existing styles
window.getComputedStyle(element).propertyName;
JS (template)
div {
height: 100%;
width: 100%;
}
CSS
> document.querySelector("div").style.height
> "100%"
> window.getComputedStyle(document.querySelector("div")).height
> "950px"
JS Console Output
Thanks to Daniel H for the example
The following example attempts to add 100px to the top of main
, but fails.
Consider the case when main
has top
set to "200px". Then
this code would update style.top
to be the invalid value of "200px100px"
let main = document.getElementById("main");
main.style.top = window.getComputedStyle(main).top + 100 + "px";
JS
A corrected version:
main.style.top = parseInt(window.getComputedStyle(main).top) + 100 + "px";
JS
.className
JS DOM's className
property corresponds to HTML class
attribute
function highlightField() {
// turn text yellow
let text = document.getElementById("text");
if (!text.className) {
text.className = "highlight";
} else if (text.className.indexOf("invalid") < 0) {
text.className += "highlight"; // awkward
}
}
JS
Works well for adding one class to the object
Somewhat clunky when dealing with multiple space-separated classes as one big string
classList
function highlightField() {
// turn text yellow
let text = document.getElementById("text");
if (!text.classList.contains("invalid")) {
text.classList.add("highlight");
}
}
JS
classList
collection has methods add
, remove
,
contains
, and toggle
to manipulate CSS classes
Similar to existing className
DOM property, but don't have to manually
split by spaces
function slideClick() {
let bullet = document.getElementById("removeme");
bullet.parentNode.removeChild(bullet);
}
JS
To remove a node from the tree, you have to do this odd thing:
obj.parentNode.remove(obj)
This is a paragraph of text with a link in it.
HTML
Element Nodes (HTML tag)
Text Nodes (text in a block element)
Attribute Nodes (attribute/value pair)
Every node's DOM object has the following properties:
Name(s) | Description |
---|---|
firstChild, lastChild | start/end of this node's list of children |
childNodes | array of all of this node's children |
nextSibling, previousSibling | neighboring nodes with the same parent |
parentNode, | the element that contains this node |
Complete list of DOM node properties
Browser incompatibility information (IE6 sucks)
This is a paragraph of text with a
link.
HTML
This is a paragraph of text with a
link.
HTML
Q: How many children does the div
have?
A: 3
<p>
Q: How many children does the paragraph have? A: 3 (text, a, text)
Q: The a
tag? A: 1 (text)
this
Visa
Mastercard
American Express
HTML
function processCard() {
let output = "";
if (document.getElementById("visa").checked) {
output = "Visa";
} else if (document.getElementById("mastercard").checked) {
output = "Mastercard";
} else {
output = "American Expresss";
}
document.getElementById("cc-output").innerHTML = "You chose " + output;
}
JS
Output
this
Mind blowing: If the same function is assigned to multiple elements, each gets its own bound copy (difference between what we read and what is executed)
window.onload = function() {
let cards = document.getElementsByName("cc");
for (let ii = 0; ii < cards.length; ii++){
cards[ii].onchange = function() {
processCard(this);
}
}
};
function processCard(obj) {
let output = this.value;
document.getElementById("cc-output").innerHTML = "You chose " + obj.value;
}
JS
You chose....
Output