up
This commit is contained in:
parent
4272b7bb13
commit
508969c13f
168 changed files with 340 additions and 10 deletions
|
@ -0,0 +1,18 @@
|
|||
Answer: **1 and 3**.
|
||||
|
||||
Both commands result in adding `text` "as text" into the `elem`.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```html run height=80
|
||||
<div id="elem1"></div>
|
||||
<div id="elem2"></div>
|
||||
<div id="elem3"></div>
|
||||
<script>
|
||||
let text = '<b>text</b>';
|
||||
|
||||
elem1.append(document.createTextNode(text));
|
||||
elem2.textContent = text;
|
||||
elem3.innerHTML = text;
|
||||
</script>
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# createTextNode vs innerHTML vs textContent
|
||||
|
||||
We have an empty DOM element `elem` and a string `text`.
|
||||
|
||||
Which of these 3 commands do exactly the same?
|
||||
|
||||
1. `elem.append(document.createTextNode(text))`
|
||||
2. `elem.innerHTML = text`
|
||||
3. `elem.textContent = text`
|
|
@ -0,0 +1,53 @@
|
|||
First, let's make HTML/CSS.
|
||||
|
||||
Each component of the time would look great in its own `<span>`:
|
||||
|
||||
```html
|
||||
<div id="clock">
|
||||
<span class="hour">hh</span>:<span class="min">mm</span>:<span class="sec">ss</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
Also we'll need CSS to color them.
|
||||
|
||||
The `update` function will refresh the clock, to be called by `setInterval` every second:
|
||||
|
||||
```js
|
||||
function update() {
|
||||
let clock = document.getElementById('clock');
|
||||
*!*
|
||||
let date = new Date(); // (*)
|
||||
*/!*
|
||||
let hours = date.getHours();
|
||||
if (hours < 10) hours = '0' + hours;
|
||||
clock.children[0].innerHTML = hours;
|
||||
|
||||
let minutes = date.getMinutes();
|
||||
if (minutes < 10) minutes = '0' + minutes;
|
||||
clock.children[1].innerHTML = minutes;
|
||||
|
||||
let seconds = date.getSeconds();
|
||||
if (seconds < 10) seconds = '0' + seconds;
|
||||
clock.children[2].innerHTML = seconds;
|
||||
}
|
||||
```
|
||||
|
||||
In the line `(*)` we every time check the current date. The calls to `setInterval` are not reliable: they may happen with delays.
|
||||
|
||||
The clock-managing functions:
|
||||
|
||||
```js
|
||||
let timerId;
|
||||
|
||||
function clockStart() { // run the clock
|
||||
timerId = setInterval(update, 1000);
|
||||
update(); // (*)
|
||||
}
|
||||
|
||||
function clockStop() {
|
||||
clearInterval(timerId);
|
||||
timerId = null;
|
||||
}
|
||||
```
|
||||
|
||||
Please note that the call to `update()` is not only scheduled in `clockStart()`, but immediately run in the line `(*)`. Otherwise the visitor would have to wait till the first execution of `setInterval`. And the clock would be empty till then.
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.hour {
|
||||
color: red
|
||||
}
|
||||
|
||||
.min {
|
||||
color: green
|
||||
}
|
||||
|
||||
.sec {
|
||||
color: blue
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="clock">
|
||||
<span class="hour">hh</span>:<span class="min">mm</span>:<span class="sec">ss</span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let timerId;
|
||||
|
||||
function update() {
|
||||
let clock = document.getElementById('clock');
|
||||
let date = new Date();
|
||||
|
||||
let hours = date.getHours();
|
||||
if (hours < 10) hours = '0' + hours;
|
||||
clock.children[0].innerHTML = hours;
|
||||
|
||||
let minutes = date.getMinutes();
|
||||
if (minutes < 10) minutes = '0' + minutes;
|
||||
clock.children[1].innerHTML = minutes;
|
||||
|
||||
let seconds = date.getSeconds();
|
||||
if (seconds < 10) seconds = '0' + seconds;
|
||||
clock.children[2].innerHTML = seconds;
|
||||
}
|
||||
|
||||
function clockStart() {
|
||||
timerId = setInterval(update, 1000);
|
||||
update(); // <-- start right now, don't wait 1 second till the first setInterval works
|
||||
}
|
||||
|
||||
function clockStop() {
|
||||
clearInterval(timerId);
|
||||
}
|
||||
|
||||
clockStart();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<!-- click on this button calls clockStart() -->
|
||||
<input type="button" onclick="clockStart()" value="Start">
|
||||
|
||||
<!-- click on this button calls clockStop() -->
|
||||
<input type="button" onclick="clockStop()" value="Stop">
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Colored clock with setInterval
|
||||
|
||||
Create a colored clock like here:
|
||||
|
||||
[iframe src="solution" height=100]
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
There are many possible solutions here.
|
||||
|
||||
For instance:
|
||||
|
||||
- `one.insertAdjacentHTML('afterend', '<li>2</li><li>3</li>')`
|
|
@ -0,0 +1,14 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Insert the HTML in the list
|
||||
|
||||
Write the code to insert `<li>2</li><li>3</li>` between two `<li>` here:
|
||||
|
||||
```html
|
||||
<ul id="ul">
|
||||
<li id="one">1</li>
|
||||
<li id="two">4</li>
|
||||
</ul>
|
||||
```
|
|
@ -0,0 +1,19 @@
|
|||
The solution is short, yet may look a bit tricky, so here I provide it with extensive comments:
|
||||
|
||||
|
||||
```js
|
||||
let sortedRows = Array.from(table.rows)
|
||||
.slice(1)
|
||||
.sort((rowA, rowB) => rowA.cells[0].innerHTML > rowB.cells[0].innerHTML ? 1 : -1);
|
||||
|
||||
table.tBodies[0].append(...sortedRows);
|
||||
```
|
||||
|
||||
1. Get all `<tr>`, like `table.querySelectorAll('tr')`, then make an array from them, cause we need array methods.
|
||||
2. The first TR (`table.rows[0]`) is actually a table header, so we take the rest by `.slice(1)`.
|
||||
3. Then sort them comparing by the content of the first `<td>` (the name field).
|
||||
4. Now insert nodes in the right order by `.append(...sortedRows)`.
|
||||
|
||||
Tables always have an implicit <tbody> element, so we need to take it and insert into it: a simple `table.append(...)` would fail.
|
||||
|
||||
Please note: we don't have to remove them, just "re-insert", they leave the old place automatically.
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<table id="table">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Surname</th>
|
||||
<th>Age</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>John</td>
|
||||
<td>Smith</td>
|
||||
<td>10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pete</td>
|
||||
<td>Brown</td>
|
||||
<td>15</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ann</td>
|
||||
<td>Lee</td>
|
||||
<td>5</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
let sortedRows = Array.from(table.rows)
|
||||
.slice(1)
|
||||
.sort((rowA, rowB) => rowA.cells[0].innerHTML > rowB.cells[0].innerHTML ? 1 : -1);
|
||||
|
||||
table.tBodies[0].append(...sortedRows);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<table id="table">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Surname</th>
|
||||
<th>Age</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>John</td>
|
||||
<td>Smith</td>
|
||||
<td>10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pete</td>
|
||||
<td>Brown</td>
|
||||
<td>15</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ann</td>
|
||||
<td>Lee</td>
|
||||
<td>5</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
// ... your code ...
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
39
2-ui/1-document/07-modifying-document/12-sort-table/task.md
Normal file
39
2-ui/1-document/07-modifying-document/12-sort-table/task.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Sort the table
|
||||
|
||||
There's a table:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Surname</th>
|
||||
<th>Age</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>John</td>
|
||||
<td>Smith</td>
|
||||
<td>10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Pete</td>
|
||||
<td>Brown</td>
|
||||
<td>15</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ann</td>
|
||||
<td>Lee</td>
|
||||
<td>5</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>...</td>
|
||||
<td>...</td>
|
||||
<td>...</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
There may be more rows in it.
|
||||
|
||||
Write the code to sort it by the `"name"` column.
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
First, let's see how not to do it:
|
||||
|
||||
```js
|
||||
function clear(elem) {
|
||||
for (let i=0; i < elem.childNodes.length; i++) {
|
||||
elem.childNodes[i].remove();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That won't work, because the call to `remove()` shifts the collection `elem.childNodes` every time, so elements every time start from index `0`. So `i` should not increase in the loop at all.
|
||||
|
||||
The `for..of` loop also does the same.
|
||||
|
||||
The right variant would be:
|
||||
|
||||
```js
|
||||
function clear(elem) {
|
||||
while (elem.firstChild) {
|
||||
elem.firstChild.remove();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And also there's a simpler variant:
|
||||
|
||||
```js
|
||||
function clear(elem) {
|
||||
elem.innerHTML = '';
|
||||
}
|
||||
```
|
20
2-ui/1-document/07-modifying-document/4-clear-elem/task.md
Normal file
20
2-ui/1-document/07-modifying-document/4-clear-elem/task.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# clear
|
||||
|
||||
Create a function `clear(elem)` that removes everything from element.
|
||||
|
||||
```html run
|
||||
<ol id="elem">
|
||||
<li>Hello</li>
|
||||
<li>World</li>
|
||||
</ol>
|
||||
|
||||
<script>
|
||||
function clear(elem) { /* your code */ }
|
||||
|
||||
clear(elem); // clears the list
|
||||
</script>
|
||||
```
|
|
@ -0,0 +1,5 @@
|
|||
The HTML in the task is incorrect. That's the matter. There may be no text inside the `<table>`, only table-specific tags.
|
||||
|
||||
The question can be easily solved by exploring the DOM in the browser tools. Then we'll see that the browser placed the text `"aaa"` *before* the table.
|
||||
|
||||
The HTML standard thoroughly specifies how to process bad HTML, and the behavior of the browser here is correct.
|
23
2-ui/1-document/07-modifying-document/5-why-aaa/task.md
Normal file
23
2-ui/1-document/07-modifying-document/5-why-aaa/task.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
importance: 1
|
||||
|
||||
---
|
||||
|
||||
# Why "ааа" remains?
|
||||
|
||||
Run the example. Why `table.remove()` does not delete the text `"aaa"`?
|
||||
|
||||
```html height=100 run
|
||||
<table id="table">
|
||||
aaa
|
||||
<tr>
|
||||
<td>Test</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
alert(table); // the table, as it should be
|
||||
|
||||
table.remove();
|
||||
// why there's still aaa in the document?
|
||||
</script>
|
||||
```
|
|
@ -0,0 +1 @@
|
|||
Please note using `textContent` to assign the `<li>` content.
|
24
2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html
Executable file
24
2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html
Executable file
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Create a list</h1>
|
||||
|
||||
<script>
|
||||
let ul = document.createElement('ul');
|
||||
document.body.append(ul);
|
||||
|
||||
while (true) {
|
||||
let data = prompt("Enter the text for the list item", "");
|
||||
|
||||
if (!data) {
|
||||
break;
|
||||
}
|
||||
|
||||
let li = document.createElement('li');
|
||||
li.textContent = data;
|
||||
ul.append(li);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
19
2-ui/1-document/07-modifying-document/6-create-list/task.md
Normal file
19
2-ui/1-document/07-modifying-document/6-create-list/task.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Create a list
|
||||
|
||||
Write an interface to create a list from user input.
|
||||
|
||||
For every list item:
|
||||
|
||||
1. Ask a user about its content using `prompt`.
|
||||
2. Create the `<li>` with it and add it to `<ul>`.
|
||||
3. Continue until the user cancels the input (by pressing `key:Esc` or CANCEL in prompt).
|
||||
|
||||
All elements should be created dynamically.
|
||||
|
||||
If a user types HTML-tags, they should be treated like a text.
|
||||
|
||||
[demo src="solution"]
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
let data = {
|
||||
"Fish": {
|
||||
"trout": {},
|
||||
"salmon": {}
|
||||
},
|
||||
|
||||
"Tree": {
|
||||
"Huge": {
|
||||
"sequoia": {},
|
||||
"oak": {}
|
||||
},
|
||||
"Flowering": {
|
||||
"redbud": {},
|
||||
"magnolia": {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function createTree(container, obj) {
|
||||
container.append(createTreeDom(obj));
|
||||
}
|
||||
|
||||
function createTreeDom(obj) {
|
||||
// if there's no children, then the call returns undefined
|
||||
// and the <ul> won't be created
|
||||
if (!Object.keys(obj).length) return;
|
||||
|
||||
let ul = document.createElement('ul');
|
||||
|
||||
for (let key in obj) {
|
||||
let li = document.createElement('li');
|
||||
li.innerHTML = key;
|
||||
|
||||
let childrenUl = createTreeDom(obj[key]);
|
||||
if (childrenUl) {
|
||||
li.append(childrenUl);
|
||||
}
|
||||
|
||||
ul.append(li);
|
||||
}
|
||||
|
||||
return ul;
|
||||
}
|
||||
|
||||
let container = document.getElementById('container');
|
||||
createTree(container, data);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
let data = {
|
||||
"Fish": {
|
||||
"trout": {},
|
||||
"salmon": {}
|
||||
},
|
||||
|
||||
"Tree": {
|
||||
"Huge": {
|
||||
"sequoia": {},
|
||||
"oak": {}
|
||||
},
|
||||
"Flowering": {
|
||||
"redbud": {},
|
||||
"magnolia": {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function createTree(container, obj) {
|
||||
container.innerHTML = createTreeText(obj);
|
||||
}
|
||||
|
||||
function createTreeText(obj) { // отдельная рекурсивная функция
|
||||
let li = '';
|
||||
for (let key in obj) {
|
||||
li += '<li>' + key + createTreeText(obj[key]) + '</li>';
|
||||
}
|
||||
if (li) {
|
||||
let ul = '<ul>' + li + '</ul>'
|
||||
}
|
||||
return ul || '';
|
||||
}
|
||||
|
||||
let container = document.getElementById('container');
|
||||
createTree(container, data);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
The easiest way to walk the object is to use recursion.
|
||||
|
||||
1. [The solution with innerHTML](sandbox:innerhtml).
|
||||
2. [The solution with DOM](sandbox:build-tree-dom).
|
|
@ -0,0 +1,68 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="tree"></div>
|
||||
|
||||
<!-- The result should be:
|
||||
<div id="tree">
|
||||
<ul>
|
||||
<li>Fish
|
||||
<ul>
|
||||
<li>trout</li>
|
||||
<li>salmon</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Trees
|
||||
<ul>
|
||||
<li>Huge
|
||||
<ul>
|
||||
<li>sequoia</li>
|
||||
<li>oak</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Flowering
|
||||
<ul>
|
||||
<li>redbud</li>
|
||||
<li>magnolia</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<script>
|
||||
let data = {
|
||||
"Fish": {
|
||||
"trout": {},
|
||||
"salmon": {}
|
||||
},
|
||||
|
||||
"Tree": {
|
||||
"Huge": {
|
||||
"sequoia": {},
|
||||
"oak": {}
|
||||
},
|
||||
"Flowering": {
|
||||
"redbud": {},
|
||||
"magnolia": {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function createTree(container, data) {
|
||||
/* your code */
|
||||
}
|
||||
|
||||
createTree(document.getElementById('tree'), data);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Create a tree from the object
|
||||
|
||||
Write a function `createTree` that creates a nested `ul/li` list from the nested object.
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
let data = {
|
||||
"Fish": {
|
||||
"trout": {},
|
||||
"salmon": {}
|
||||
},
|
||||
|
||||
"Tree": {
|
||||
"Huge": {
|
||||
"sequoia": {},
|
||||
"oak": {}
|
||||
},
|
||||
"Flowering": {
|
||||
"redbud": {},
|
||||
"magnolia": {}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
The syntax:
|
||||
|
||||
```js
|
||||
let container = document.getElementById('container');
|
||||
*!*
|
||||
createTree(container, data); // creates the tree in the container
|
||||
*/!*
|
||||
```
|
||||
|
||||
The result (tree) should look like this:
|
||||
|
||||
[iframe border=1 src="build-tree-dom"]
|
||||
|
||||
Choose one of two ways of solving this task:
|
||||
|
||||
1. Create the HTML for the tree and then assign to `container.innerHTML`.
|
||||
2. Create tree nodes and append with DOM methods.
|
||||
|
||||
Would be great if you could do both.
|
||||
|
||||
P.S. The tree should not have "extra" elements like empty `<ul></ul>` for the leaves.
|
|
@ -0,0 +1 @@
|
|||
To append text to each `<li>` we can alter the text node `data`.
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
<li>Animals
|
||||
<ul>
|
||||
<li>Mammals
|
||||
<ul>
|
||||
<li>Cows</li>
|
||||
<li>Donkeys</li>
|
||||
<li>Dogs</li>
|
||||
<li>Tigers</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Other
|
||||
<ul>
|
||||
<li>Snakes</li>
|
||||
<li>Birds</li>
|
||||
<li>Lizards</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Fishes
|
||||
<ul>
|
||||
<li>Aquarium
|
||||
<ul>
|
||||
<li>Guppy</li>
|
||||
<li>Angelfish</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Sea
|
||||
<ul>
|
||||
<li>Sea trout</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
let lis = document.getElementsByTagName('li');
|
||||
|
||||
for (let li of lis) {
|
||||
// получить количество детей
|
||||
let childCount = li.getElementsByTagName('li').length;
|
||||
if (!childCount) continue;
|
||||
|
||||
// добавить кол-во детей к текстовому узлу
|
||||
li.firstChild.data += ' [' + childCount + ']';
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
<li>Animals
|
||||
<ul>
|
||||
<li>Mammals
|
||||
<ul>
|
||||
<li>Cows</li>
|
||||
<li>Donkeys</li>
|
||||
<li>Dogs</li>
|
||||
<li>Tigers</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Other
|
||||
<ul>
|
||||
<li>Snakes</li>
|
||||
<li>Birds</li>
|
||||
<li>Lizards</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Fishes
|
||||
<ul>
|
||||
<li>Aquarium
|
||||
<ul>
|
||||
<li>Guppy</li>
|
||||
<li>Angelfish</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Sea
|
||||
<ul>
|
||||
<li>Sea trout</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
// ... your code ...
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
13
2-ui/1-document/07-modifying-document/8-tree-count/task.md
Normal file
13
2-ui/1-document/07-modifying-document/8-tree-count/task.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Show descendants in a tree
|
||||
|
||||
There's a tree organized as nested `ul/li`.
|
||||
|
||||
Write the code that adds to each `<li>` the number of its descendants. Skip leaves (nodes without children).
|
||||
|
||||
The result:
|
||||
|
||||
[iframe border=1 src="solution"]
|
|
@ -0,0 +1,9 @@
|
|||
We'll create the table as a string: `"<table>...</table>"`, and then assign it to `innerHTML`.
|
||||
|
||||
The algorithm:
|
||||
|
||||
1. Create the table header with `<th>` and weekday names.
|
||||
1. Create the date object `d = new Date(year, month-1)`. That's the first day of `month` (taking into account that months in JavaScript start from `0`, not `1`).
|
||||
2. First few cells till the first day of the month `d.getDay()` may be empty. Let's fill them in with `<td></td>`.
|
||||
3. Increase the day in `d`: `d.setDate(d.getDate()+1)`. If `d.getMonth()` is not yet the next month, then add the new cell `<td>` to the calendar. If that's a Sunday, then add a newline <code>"</tr><tr>"</code>.
|
||||
4. If the month has finished, but the table row is not yet full, add empty `<td>` into it, to make it square.
|
|
@ -0,0 +1,79 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 1px solid black;
|
||||
padding: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
background-color: #E6E6E6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div id="calendar"></div>
|
||||
|
||||
<script>
|
||||
function createCalendar(elem, year, month) {
|
||||
|
||||
let mon = month - 1; // months in JS are 0..11, not 1..12
|
||||
let d = new Date(year, mon);
|
||||
|
||||
let table = '<table><tr><th>MO</th><th>TU</th><th>WE</th><th>TH</th><th>FR</th><th>SA</th><th>SU</th></tr><tr>';
|
||||
|
||||
// spaces for the first row
|
||||
// from Monday till the first day of the month
|
||||
// * * * 1 2 3 4
|
||||
for (let i = 0; i < getDay(d); i++) {
|
||||
table += '<td></td>';
|
||||
}
|
||||
|
||||
// <td> with actual dates
|
||||
while (d.getMonth() == mon) {
|
||||
table += '<td>' + d.getDate() + '</td>';
|
||||
|
||||
if (getDay(d) % 7 == 6) { // sunday, last day of week - newline
|
||||
table += '</tr><tr>';
|
||||
}
|
||||
|
||||
d.setDate(d.getDate() + 1);
|
||||
}
|
||||
|
||||
// add spaces after last days of month for the last row
|
||||
// 29 30 31 * * * *
|
||||
if (getDay(d) != 0) {
|
||||
for (let i = getDay(d); i < 7; i++) {
|
||||
table += '<td></td>';
|
||||
}
|
||||
}
|
||||
|
||||
// close the table
|
||||
table += '</tr></table>';
|
||||
|
||||
elem.innerHTML = table;
|
||||
}
|
||||
|
||||
function getDay(date) { // get day number from 0 (monday) to 6 (sunday)
|
||||
let day = date.getDay();
|
||||
if (day == 0) day = 7; // make Sunday (0) the last day
|
||||
return day - 1;
|
||||
}
|
||||
|
||||
createCalendar(calendar, 2012, 9);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 1px solid black;
|
||||
padding: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
background-color: #E6E6E6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<div id="calendar"></div>
|
||||
|
||||
<script>
|
||||
function createCalendar(elem, year, month) {
|
||||
// ...your code that generates the calndar in elem...
|
||||
}
|
||||
|
||||
createCalendar(calendar, 2012, 9);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,17 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Create a calendar
|
||||
|
||||
Write a function `createCalendar(elem, year, month)`.
|
||||
|
||||
The call should create a calendar for the given year/month and put it inside `elem`.
|
||||
|
||||
The calendar should be a table, where a week is `<tr>`, and a day is `<td>`. The table top should be `<th>` with weekday names: the first day should be Monday, and so on till Sunday.
|
||||
|
||||
For instance, `createCalendar(cal, 2012, 9)` should generate in <code><div id='cal'></div></code> the following calendar:
|
||||
|
||||
[iframe height=210 src="solution"]
|
||||
|
||||
P.S. For this task it's enough to generate the calendar, should not yet be clickable.
|
484
2-ui/1-document/07-modifying-document/article.md
Normal file
484
2-ui/1-document/07-modifying-document/article.md
Normal file
|
@ -0,0 +1,484 @@
|
|||
# Modifying DOM
|
||||
|
||||
Modifying DOM is the key to create "live" pages.
|
||||
|
||||
Here we'll see how to create new elements "on the fly" and modify the existing page content.
|
||||
|
||||
There are many methods for that. First we'll see a simple example and then explain them.
|
||||
|
||||
[cut]
|
||||
|
||||
## Example: show a message
|
||||
|
||||
For the start, let's see how to add a message on the page, that looks nicer than `alert`.
|
||||
|
||||
Here's how it will look:
|
||||
|
||||
```html autorun height="80"
|
||||
<style>
|
||||
.alert {
|
||||
padding: 15px;
|
||||
border: 1px solid #d6e9c6;
|
||||
border-radius: 4px;
|
||||
color: #3c763d;
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
</style>
|
||||
|
||||
*!*
|
||||
<div class="alert">
|
||||
<strong>Hi there!</strong> You've read an important message.
|
||||
</div>
|
||||
*/!*
|
||||
```
|
||||
|
||||
Now let's create the same `div` with Javascript.
|
||||
|
||||
## Creating an element
|
||||
|
||||
|
||||
To create DOM nodes, there are two methods:
|
||||
|
||||
`document.createElement(tag)`
|
||||
: Creates a new element with the given tag:
|
||||
|
||||
```js
|
||||
let div = document.createElement('div');
|
||||
```
|
||||
|
||||
`document.createTextNode(text)`
|
||||
: Creates a new *text node* with the given text:
|
||||
|
||||
```js
|
||||
let textNode = document.createTextNode('Here I am');
|
||||
```
|
||||
|
||||
### Creating the message
|
||||
|
||||
In our case we want to make a `div`, add classes and the message into it:
|
||||
|
||||
```js
|
||||
let div = document.createElement('div');
|
||||
div.className = "alert alert-success";
|
||||
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
|
||||
```
|
||||
|
||||
After that, we have a ready DOM element. Right now it's in the variable `div`, but not yet seen, because not inserted into the page.
|
||||
|
||||
## Insertion methods
|
||||
|
||||
To make the `div` show up, we need to insert it somewhere into `document`.
|
||||
|
||||
Let's say we want to insert it into `parentElem`, like `let parentElem=document.body`.
|
||||
|
||||
There exist following methods to insert a node:
|
||||
|
||||
`parentElem.appendChild(elem)`
|
||||
: Appends `elem` as the last child of `parentElem`.
|
||||
|
||||
The following example adds a new `<li>` to the end of `<ol>`:
|
||||
|
||||
```html run height=100
|
||||
<ol id="list">
|
||||
<li>0</li>
|
||||
<li>1</li>
|
||||
<li>2</li>
|
||||
</ol>
|
||||
|
||||
<script>
|
||||
let newLi = document.createElement('li');
|
||||
newLi.innerHTML = 'Hello, world!';
|
||||
|
||||
list.appendChild(newLi);
|
||||
</script>
|
||||
```
|
||||
|
||||
`parentElem.insertBefore(elem, nextSibling)`
|
||||
: Inserts `elem` before `nextSibling` into `parentElem`.
|
||||
|
||||
The following code inserts a new list item before the second `<li>`:
|
||||
|
||||
```html run height=100
|
||||
<ol id="list">
|
||||
<li>0</li>
|
||||
<li>1</li>
|
||||
<li>2</li>
|
||||
</ol>
|
||||
<script>
|
||||
let newLi = document.createElement('li');
|
||||
newLi.innerHTML = 'Hello, world!';
|
||||
|
||||
*!*
|
||||
list.insertBefore(newLi, list.children[1]);
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
To insert as the first element, we can do like this:
|
||||
|
||||
```js
|
||||
list.insertBefore(newLi, list.firstChild);
|
||||
```
|
||||
|
||||
`parentElem.replaceChild(elem, oldChild)`
|
||||
: Replaces `oldChild` with `elem` among children of `parentElem`.
|
||||
|
||||
All these methods return the inserted node. In other words, `parentElem.appendChild(elem)` returns `elem`. But usually the returned value is not used, we just run the method.
|
||||
|
||||
For our example, it would be like this:
|
||||
|
||||
```html run height="80"
|
||||
<style>
|
||||
.alert {
|
||||
padding: 15px;
|
||||
border: 1px solid #d6e9c6;
|
||||
border-radius: 4px;
|
||||
color: #3c763d;
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
*!*
|
||||
let div = document.createElement('div');
|
||||
div.className = "alert alert-success";
|
||||
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
|
||||
|
||||
document.body.appendChild(div);
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
These methods are "old school": they exist from the ancient times and we can meet them in many old scripts.
|
||||
|
||||
Unfortunately, there are some tasks that are hard to solve with them.
|
||||
|
||||
For instance, how to insert *html* if we have it as a string? Or, given a node, how to insert something not into it, but *before* it?
|
||||
|
||||
Of course, all that is solvable, but not in an elegant way.
|
||||
|
||||
So there exist two other sets of insertion methods to handle all cases easily.
|
||||
|
||||
### prepend/append/before/after
|
||||
|
||||
This set of methods provides more flexible insertions:
|
||||
|
||||
- `node.append(...nodes or strings)` -- append nodes or strings at the end of `node`,
|
||||
- `node.prepend(...nodes or strings)` -- insert nodes or strings into the beginning of `node`,
|
||||
- `node.before(...nodes or strings)` –- insert nodes or strings before the `node`,
|
||||
- `node.after(...nodes or strings)` –- insert nodes or strings after the `node`,
|
||||
- `node.replaceWith(...nodes or strings)` –- replaces `node` with the given nodes or strings.
|
||||
|
||||
Let's say we have a list, like this:
|
||||
|
||||
```html autorun
|
||||
<ol id="ol">
|
||||
<li>0</li>
|
||||
<li>1</li>
|
||||
<li>2</li>
|
||||
</ol>
|
||||
|
||||
<script>
|
||||
ol.before('before');
|
||||
ol.after('after');
|
||||
|
||||
let prepend = document.createElement('li');
|
||||
prepend.innerHTML = 'prepend';
|
||||
ol.prepend(prepend);
|
||||
|
||||
let append = document.createElement('li');
|
||||
append.innerHTML = 'append';
|
||||
ol.append(append);
|
||||
</script>
|
||||
```
|
||||
|
||||
Here's where the insertions will go:
|
||||
|
||||

|
||||
|
||||
So the final list will be:
|
||||
|
||||
```html
|
||||
before
|
||||
<ol id="ol">
|
||||
<li>prepend</li>
|
||||
<li>0</li>
|
||||
<li>1</li>
|
||||
<li>2</li>
|
||||
<li>append</li>
|
||||
</ol>
|
||||
after
|
||||
```
|
||||
|
||||
These methods can insert a list of nodes and text pieces. But please note: all text is inserted *as text*.
|
||||
|
||||
For instance, here a string and an element are inserted:
|
||||
|
||||
```html run
|
||||
<div id="div"></div>
|
||||
<script>
|
||||
div.before('<p>Hello</p>', document.createElement('hr'));
|
||||
</script>
|
||||
```
|
||||
|
||||
The final HTML would be:
|
||||
|
||||
```html run
|
||||
*!*
|
||||
<p>Hello</p>
|
||||
*/!*
|
||||
<hr>
|
||||
<div id="div"></div>
|
||||
```
|
||||
|
||||
In other words, strings are inserted exactly "as text", in a safe way, like `elem.textContent` does it.
|
||||
|
||||
So, these methods allow to insert DOM nodes or text pieces at given places.
|
||||
|
||||
But what if we want to insert HTML "as html", with all tags and stuff working, like `elem.innerHTML` does it?
|
||||
|
||||
### insertAdjacentHTML/Text/Element
|
||||
|
||||
There's a versatile method `elem.insertAdjacentHTML(where, html)`.
|
||||
|
||||
The first parameter is a string, specifying where to insert, must be one of the following:
|
||||
|
||||
- `"beforebegin"` -- insert `html` before `elem`,
|
||||
- `"afterbegin"` -- insert `html` into `elem`, at the beginning,
|
||||
- `"beforeend"` -- insert `html` into `elem`, at the end,
|
||||
- `"afterend"` -- insert `html` after `elem`.
|
||||
|
||||
The second parameter `html` is a HTML string, inserted "as is".
|
||||
|
||||
For instance:
|
||||
|
||||
```html run
|
||||
<div id="div"></div>
|
||||
<script>
|
||||
div.insertAdjacentHTML('beforebegin', '<p>Hello</p>');
|
||||
</script>
|
||||
```
|
||||
|
||||
...Would lead to:
|
||||
|
||||
```html run
|
||||
<p>Hello</p>
|
||||
<div id="div"></div>
|
||||
```
|
||||
|
||||
That's how we can append an arbitrary HTML to our page.
|
||||
|
||||
Here's the picture of insertion variants:
|
||||
|
||||

|
||||
|
||||
We definitely can notice similarities between this and the previous picture. The insertion points are actually the same, but here we can insert HTML.
|
||||
|
||||
The method has two brothers:
|
||||
|
||||
- `elem.insertAdjacentText(where, text)` -- the same syntax, but a string of `text` in inserted "as text" instead of HTML,
|
||||
- `elem.insertAdjacentElement(where, elem)` -- the same syntax, but inserts an element.
|
||||
|
||||
They exist mainly to make the syntax "uniform". In practice, most of time only `insertAdjacentHTML` is used, because for elements and text we have methods `append/prepend/before/after` -- they are just shorter to write.
|
||||
|
||||
So here's an alternative variant of showing a message:
|
||||
|
||||
```html run
|
||||
<style>
|
||||
.alert {
|
||||
padding: 15px;
|
||||
border: 1px solid #d6e9c6;
|
||||
border-radius: 4px;
|
||||
color: #3c763d;
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.body.insertAdjacentHTML("afterbegin", `<div class="alert alert-success">
|
||||
<strong>Hi there!</strong> You've read an important message.
|
||||
</div>`);
|
||||
</script>
|
||||
```
|
||||
|
||||
## Cloning nodes: cloneNode
|
||||
|
||||
How to insert one more similar message?
|
||||
|
||||
We could do a message-generating function and put the code there. But the alternative way would be to *clone* the existing `div` and modify the text inside it.
|
||||
|
||||
Sometimes when we have a big element, that may be faster and simpler.
|
||||
|
||||
The call `elem.cloneNode(true)` creates a "deep" clone of the element -- with all attributes and subelements. If we call it with `false`, then there would be no child elements.
|
||||
|
||||
An example of copying the message:
|
||||
|
||||
```html run height="120"
|
||||
<style>
|
||||
.alert {
|
||||
padding: 15px;
|
||||
border: 1px solid #d6e9c6;
|
||||
border-radius: 4px;
|
||||
color: #3c763d;
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="alert" id="div">
|
||||
<strong>Hi there!</strong> You've read an important message.
|
||||
</div>
|
||||
|
||||
<script>
|
||||
*!*
|
||||
let div2 = div.cloneNode(true); // clone the message
|
||||
div2.querySelector('strong').innerHTML = 'Bye there!'; // change the clone
|
||||
|
||||
div.after(div2); // show the clone after the existing div
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
## Removal methods
|
||||
|
||||
To remove nodes, there are following methods:
|
||||
|
||||
|
||||
`parentElem.removeChild(node)`
|
||||
: Removes `elem` from `parentElem` (assuming it's a child).
|
||||
|
||||
`node.remove()`
|
||||
: Removes the `node` from its place.
|
||||
|
||||
We can easily see that the second method is much shorter. The first one exists for historical reasons.
|
||||
|
||||
````smart
|
||||
If we want to *move* an element to another place -- there's no need to remove it from the old one.
|
||||
|
||||
**All insertion methods automatically remove the node from the old place.**
|
||||
|
||||
For instance, let's swap elements:
|
||||
|
||||
```html run height=50
|
||||
<div id="first">First</div>
|
||||
<div id="second">Second</div>
|
||||
<script>
|
||||
// no need to call remove
|
||||
second.after(first); // after second insert first
|
||||
</script>
|
||||
```
|
||||
````
|
||||
|
||||
Let's make our message to disappear after a second:
|
||||
|
||||
```html run untrusted
|
||||
<style>
|
||||
.alert {
|
||||
padding: 15px;
|
||||
border: 1px solid #d6e9c6;
|
||||
border-radius: 4px;
|
||||
color: #3c763d;
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let div = document.createElement('div');
|
||||
div.className = "alert alert-success";
|
||||
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
|
||||
|
||||
document.body.append(div);
|
||||
*!*
|
||||
setTimeout(div => div.remove(), 1000);
|
||||
// or setTimeout(div => document.body.removeChild(div), 1000);
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
||||
## A word about "document.write"
|
||||
|
||||
There's one more, quite ancient method of adding something to a web-page: `document.write`.
|
||||
|
||||
The syntax:
|
||||
|
||||
```html run
|
||||
<p>Somewhere in the page...</p>
|
||||
*!*
|
||||
<script>
|
||||
document.write('<b>Hello from JS</b>');
|
||||
</script>
|
||||
*/!*
|
||||
<p>The end</p>
|
||||
```
|
||||
|
||||
The call to `document.write(html)` writes the `html` into page "right here and now". The `html` string can be dynamically generated, so it's kind of flexible.
|
||||
|
||||
The method comes from times when there were no DOM, no standards... Really old times. It still lives, because there are scripts using it.
|
||||
|
||||
In modern scripts we can rarely see it, because of the important limitation.
|
||||
|
||||
**The call to `document.write` only works while the page is loading.**
|
||||
|
||||
If we call it after it, the existing document will be erased.
|
||||
|
||||
For instance:
|
||||
|
||||
```html run
|
||||
<p>After one second the contents of this page will be replaced...</p>
|
||||
*!*
|
||||
<script>
|
||||
// document.write after 1 second
|
||||
setTimeout(() => document.write('<b>...By this.</b>'), 1000);
|
||||
</script>
|
||||
*/!*
|
||||
```
|
||||
|
||||
The method `document.write` works at the "reading HTML" phase. It appends something to the page and the browser consumes it along with the rest of HTML.
|
||||
|
||||
So it's kind of unusable at "after loaded" stage, unlike other DOM methods we covered above.
|
||||
|
||||
That was the downside.
|
||||
|
||||
The upside -- it works blazingly fast, because it writes directly into the text, without interfering with complex DOM structures.
|
||||
|
||||
So if we need to add a lot of text into HTML dynamically, and we're at page loading phase, and the speed matters, it may help. But in practice that's a really rare use case. Mostly we can see this method in scripts just because they are old.
|
||||
|
||||
## Summary
|
||||
|
||||
Methods to create new nodes:
|
||||
|
||||
- `document.createElement(tag)` -- creates an element with the given tag,
|
||||
- `document.createTextNode(value)` -- creates a text node (rarely used),
|
||||
- `elem.cloneNode(deep)` -- clones the element, if `deep==true` then with all descendants.
|
||||
|
||||
Insertion and removal of nodes:
|
||||
|
||||
- From the parent:
|
||||
- `parent.appendChild(elem)`
|
||||
- `parent.insertBefore(elem, nextSibling)`
|
||||
- `parent.removeChild(elem)`
|
||||
- `parent.replaceChild(newElem, elem)`
|
||||
|
||||
all thes methods return `elem`.
|
||||
|
||||
- Given a node:
|
||||
- `node.append(...nodes or strings)` -- insert into `node`, at the end,
|
||||
- `node.prepend(...nodes or strings)` -- insert into `node`, at the beginning,
|
||||
- `node.before(...nodes or strings)` –- insert right before `node`,
|
||||
- `node.after(...nodes or strings)` –- insert right after `node`,
|
||||
- `node.replaceWith(...nodes or strings)` –- replace `node`.
|
||||
- `node.remove()` –- remove the `node`.
|
||||
|
||||
All these methods accept a list of DOM nodes or text strings. Text strings are inserted "as text".
|
||||
|
||||
- To insert HTML: `elem.insertAdjacentHTML(where, html)`, inserts depending on where:
|
||||
- `"beforebegin"` -- insert `html` right before `elem`,
|
||||
- `"afterbegin"` -- insert `html` into `elem`, at the beginning,
|
||||
- `"beforeend"` -- insert `html` into `elem`, at the end,
|
||||
- `"afterend"` -- insert `html` right after `elem`.
|
||||
|
||||
Also there are similar methods `elem.insertAdjacentText` and `elem.insertAdjacentElement`, they insert text strings and elements, but they are rarely used.
|
||||
|
||||
- To append HTML to the page before it has finished loading:
|
||||
- `document.write(html)`
|
||||
|
||||
After the page is loaded such call erases the document. Mostly seen in old scripts.
|
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
BIN
2-ui/1-document/07-modifying-document/insert-adjacent.png
Normal file
BIN
2-ui/1-document/07-modifying-document/insert-adjacent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
2-ui/1-document/07-modifying-document/insert-adjacent@2x.png
Normal file
BIN
2-ui/1-document/07-modifying-document/insert-adjacent@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
Loading…
Add table
Add a link
Reference in a new issue