JavaScript Hide and Seek
Problem Statement
One of the most essential skills in our web development toolbox is finding elements in the DOM. We need the help of a couple methods to make the process quick and easy.
Objectives
- Use document.querySelectorAll to find nested nodes
- Change the value of the correct DOM nodes
Introduction
To practice finding elements in the DOM, we're going to make use of two methods
that are useful for navigating the DOM: querySelector()
and
querySelectorAll()
.
querySelector()
querySelector()
takes one argument, a string of selectors, and
returns the first element that matches these selectors. Given a document like
<body>
<div>
Hello!
</div>
<div>
Goodbye!
</div>
</body>
If we called document.querySelector('div')
, the method would return the first
div
(whose content is "Hello!").
Selectors aren't limited to tag names, though (otherwise why not just use document.getElementsByTagName('div')[0]
?). We can get very fancy.
<body>
<div>
<ul class="ranked-list">
<li>1</li>
<li>
<div>
<ul>
<li>2</li>
</ul>
</div>
</li>
<li>3</li>
</ul>
</div>
<div>
<ul class="unranked-list">
<li>6</li>
<li>2</li>
<li>
<div>4</div>
</li>
</ul>
</div>
<script>
// get <li>2</li>
const li2 = document.querySelector('ul.ranked-list li ul li')
// get <div>4</div>
const div4 = document.querySelector('ul.unranked-list li div')
</script>
</body>
In the above example, the first query says, "Starting from document
(the
object we've called querySelector()
on), find a ul
with a className
of
ranked-list
(the .
is for className
). Then find an li
that is a child of
that ul
. Then find a ul
that is a child (but not necessarily a direct
descendant) of that li
. Finally, find an li
that is a child of that (second)
ul
."
NOTE: The HTML property class
is referred to as className
in JavaScript.
It's... unfortunate.
What, then, does the second call to querySelector()
say? Puzzle it out for a
bit, and then read on.
Puzzle a bit longer!
Just a bit longer!
Okay, the second call says, "Starting from document
, find a ul
with a
className
of unranked-list
. Then find an li
descended from
ul.unranked- list
and a div
descended from that li
."
Interlude: Selectors
Now is probably a good time to brush up on selectors. Play around on the MDN page, then come back when you're ready.
querySelectorAll()
querySelectorAll
works a lot like querySelector()
— it accepts a selector
as its argument, and it searches starting from the element that it's called on
(or from document
) — but instead of returning the first match, it returns a
NodeList (which, remember, is not an Array) of all matching elements.
Given a document like
<main id="app">
<ul class="ranked-list">
<li>1</li>
<li>2</li>
</ul>
<ul class="ranked-list">
<li>10</li>
<li>11</li>
</ul>
</main>
If we called document.getElementById('app').querySelectorAll('ul.ranked-list li')
, we'd get back a list of Nodes corresponding to <li>1</li>
,
<li>2</li>
, <li>10</li>
, <li>11</li>
, we could change the content of these
li
s like so:
const lis = document
.getElementById('app')
.querySelectorAll('ul.ranked-list li');
for (let i = 0; i < lis.length; i++) {
lis[i].innerHTML = (i + 1).toString();
}
Now our li
s, even though they're children of two separate ul
s, will count up
from 1 to 4.
Using this loop construct, we could even, say, call querySelector()
or
querySelectorAll()
on these children to look deeper and deeper into a nested
structure... (hint!).
Instructions
In index.html
, you'll see that we've set up a basic document for you. We'll be
testing against this document, but you should still write your code in
index.js
. We'll handle loading everything up for you.
-
Define a function
getFirstSelector(selector)
, which accepts a selector and returns the first element that matches. -
Define a function
nestedTarget()
that pulls a.target
out of#nested
(#
is used for IDs in selectors — but you knew that because you read the docs, right? :) ). (Note that inindex.html
#nested
and.target
just happen to bediv
s. This method should work with arbitrary elements.) -
Define a function
increaseRankBy(n)
that increases the ranks in all of the.ranked-list
s byn
. (You might need to make use ofparseInt()
-
Define a function
deepestChild()
that pulls out the most deeply nested child fromdiv#grand-node
. (Remember, you can iterate over elements and callquerySelector()
andquerySelectorAll()
on them. This is challenging to implement correctly, but not beyond your ability!)
HINT: Your solution for deepestChild()
does not need to be totally
generic; we don't expect it to work in every case. For example, we know that
div#grand-node
has only one node at each level — for this lab, you can solve
for that case, and not worry about a case where there are sibling nodes.
ADDITIONAL HINT: Remember learning about breadth-first search? A similar technique might come in handy here.