This commit is contained in:
Ilya Kantor 2017-02-28 12:54:48 +03:00
parent 4272b7bb13
commit 508969c13f
168 changed files with 340 additions and 10 deletions

View file

@ -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>
```

View file

@ -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`

View file

@ -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.

View file

@ -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>

View file

@ -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>

View file

@ -0,0 +1,9 @@
importance: 4
---
# Colored clock with setInterval
Create a colored clock like here:
[iframe src="solution" height=100]

View file

@ -0,0 +1,6 @@
There are many possible solutions here.
For instance:
- `one.insertAdjacentHTML('afterend', '<li>2</li><li>3</li>')`

View file

@ -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>
```

View file

@ -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.

View file

@ -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>

View file

@ -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>

View 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.

View file

@ -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 = '';
}
```

View 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>
```

View file

@ -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.

View 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>
```

View file

@ -0,0 +1 @@
Please note using `textContent` to assign the `<li>` content.

View 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>

View 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"]

View file

@ -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>

View file

@ -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>

View file

@ -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).

View file

@ -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>

View file

@ -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.

View file

@ -0,0 +1 @@
To append text to each `<li>` we can alter the text node `data`.

View file

@ -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>

View file

@ -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>

View 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"]

View file

@ -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>"&lt;/tr&gt;&lt;tr&gt;"</code>.
4. If the month has finished, but the table row is not yet full, add empty `<td>` into it, to make it square.

View file

@ -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>

View file

@ -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>

View file

@ -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>&lt;div id='cal'&gt;&lt;/div&gt;</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.

View 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:
![](before-prepend-append-after.png)
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
*!*
&lt;p&gt;Hello&lt;/p&gt;
*/!*
<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:
![](insert-adjacent.png)
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB