up
This commit is contained in:
parent
20784e7f26
commit
7019d1470d
48 changed files with 1019 additions and 464 deletions
|
@ -130,7 +130,7 @@ The example above can be split into two scripts to work:
|
|||
|
||||
- We can use a `<script>` tag to add JavaScript code to the page.
|
||||
- The `type` and `language` attributes are not required.
|
||||
- Scripts in an external file can be inserted on the page via `<script src="path"></script>`.
|
||||
- A script in an external file can be inserted with `<script src="path/to/script.js"></script>`.
|
||||
|
||||
|
||||
There is much more about browser scripts and their interaction with the web-page. But let's keep in mind that this part of the tutorial is devoted to Javascript language. So we shouldn't distract ourselves from it. We'll be using a browser as a way to run Javascript, very convenient for online reading, but yet one of many.
|
||||
|
|
|
@ -218,7 +218,7 @@ let age = prompt('Your age?', 18);
|
|||
|
||||
switch (age) {
|
||||
case 18:
|
||||
alert("Won't work"); // the result of prompt is a string, not a number число
|
||||
alert("Won't work"); // the result of prompt is a string, not a number
|
||||
|
||||
case "18":
|
||||
alert("This works!"");
|
||||
|
|
|
@ -1,50 +1,48 @@
|
|||
# Ответ
|
||||
|
||||
Вы могли заметить следующие недостатки, сверху-вниз:
|
||||
You could note the following:
|
||||
|
||||
```js no-beautify
|
||||
function pow(x,n) // <- отсутствует пробел между аргументами
|
||||
{ // <- фигурная скобка на отдельной строке
|
||||
var result=1; // <- нет пробелов вокруг знака =
|
||||
for(var i=0;i<n;i++) {result*=x;} // <- нет пробелов
|
||||
// содержимое скобок { ... } лучше вынести на отдельную строку
|
||||
function pow(x,n) // <- no space between arguments
|
||||
{ // <- figure bracket on a separate line
|
||||
let result=1; // <- no spaces to the both sides of =
|
||||
for(let i=0;i<n;i++) {result*=x;} // <- no spaces
|
||||
// the contents of { ... } should be on a new line
|
||||
return result;
|
||||
}
|
||||
|
||||
x=prompt("x?",'') // <- не объявлена переменная, нет пробелов, ;
|
||||
n=prompt("n?",'')
|
||||
if (n<0) // <- нет пробелов, стоит добавить вертикальную отбивку
|
||||
{ // <- фигурная скобка на отдельной строке
|
||||
// ниже - слишком длинная строка, нет пробелов
|
||||
alert('Степень '+n+'не поддерживается, введите целую степень, большую 0');
|
||||
let x=prompt("x?",''), n=prompt("n?",'') // <-- technically possible,
|
||||
// but better make it 2 lines, also there's no spaces and ;
|
||||
if (n<0) // <- no spaces inside (n < 0), and should be extra line above it
|
||||
{ // <- figure bracket on a separate line
|
||||
// below - a long line, may be worth to split into 2 lines
|
||||
alert(`Power ${n} is not supported, please enter an integer number greater than zero`);
|
||||
}
|
||||
else // <- можно на одной строке } else {
|
||||
else // <- could write it on a single line like "} else {"
|
||||
{
|
||||
alert(pow(x,n)) // нет точки с запятой
|
||||
alert(pow(x,n)) // no spaces and ;
|
||||
}
|
||||
```
|
||||
|
||||
Исправленный вариант:
|
||||
The fixed variant:
|
||||
|
||||
```js
|
||||
function pow(x, n) {
|
||||
var result = 1;
|
||||
let result = 1;
|
||||
|
||||
for (var i = 0; i < n; i++) {
|
||||
for (let i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
var x = prompt("x?", "");
|
||||
var n = prompt("n?", "");
|
||||
let x = prompt("x?", "");
|
||||
let n = prompt("n?", "");
|
||||
|
||||
if (n < 0) {
|
||||
alert('Степень ' + n +
|
||||
'не поддерживается, введите целую степень, большую 0');
|
||||
alert(`Power ${n} is not supported,
|
||||
please enter an integer number greater than zero`);
|
||||
} else {
|
||||
alert( pow(x, n) );
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -2,23 +2,22 @@ importance: 4
|
|||
|
||||
---
|
||||
|
||||
# Ошибки в стиле
|
||||
# Bad style
|
||||
|
||||
Какие недостатки вы видите в стиле этого примера?
|
||||
What's wrong with the code style below?
|
||||
|
||||
```js no-beautify
|
||||
function pow(x,n)
|
||||
{
|
||||
var result=1;
|
||||
for(var i=0;i<n;i++) {result*=x;}
|
||||
let result=1;
|
||||
for(let i=0;i<n;i++) {result*=x;}
|
||||
return result;
|
||||
}
|
||||
|
||||
x=prompt("x?",'')
|
||||
n=prompt("n?",'')
|
||||
let x=prompt("x?",''), n=prompt("n?",'')
|
||||
if (n<=0)
|
||||
{
|
||||
alert('Степень '+n+'не поддерживается, введите целую степень, большую 0');
|
||||
alert(`Power ${n} is not supported, please enter an integer number greater than zero`);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -26,3 +25,4 @@ else
|
|||
}
|
||||
```
|
||||
|
||||
Fix it.
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
function pow(x, n) {
|
||||
if (n < 0) return NaN;
|
||||
if (Math.round(n) != n) return NaN;
|
||||
|
||||
var result = 1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
/* исправьте этот код */
|
||||
function pow(x, n) {
|
||||
var result = 1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
describe("pow", function() {
|
||||
|
||||
describe("возводит x в степень n", function() {
|
||||
|
||||
function makeTest(x) {
|
||||
var expected = x * x * x;
|
||||
it("при возведении " + x + " в степень 3 результат: " + expected, function() {
|
||||
assert.equal(pow(x, 3), expected);
|
||||
});
|
||||
}
|
||||
|
||||
for (var x = 1; x <= 5; x++) {
|
||||
makeTest(x);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it("при возведении в отрицательную степень результат NaN", function() {
|
||||
assert(isNaN(pow(2, -1)), "pow(2, -1) не NaN");
|
||||
});
|
||||
|
||||
it("при возведении в дробную степень результат NaN", function() {
|
||||
assert(isNaN(pow(2, 1.5)), "pow(2, -1.5) не NaN");
|
||||
});
|
||||
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
|
||||
```js
|
||||
function pow(x, n) {
|
||||
*!*
|
||||
if (n < 0) return NaN;
|
||||
if (Math.round(n) != n) return NaN;
|
||||
*/!*
|
||||
|
||||
var result = 1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Сделать pow по спецификации
|
||||
|
||||
Исправьте код функции `pow`, чтобы тесты проходили.
|
||||
|
||||
Для этого ниже в задаче вы найдёте ссылку на песочницу.
|
||||
|
||||
Она содержит HTML с тестами. Обратите внимание, что HTML-страница в ней короче той, что обсуждалась в статье <info:testing>. Это потому что библиотеки Chai, Mocha и Sinon объединены в один файл:
|
||||
|
||||
```html
|
||||
<script src="https://js.cx/test/libs.js"></script>
|
||||
```
|
||||
|
||||
Этот файл содержит код библиотек, стили, настройки для них и запуск `mocha.run` по окончании загрузки страницы. Если нет элемента с `id="mocha"`, то результаты выводятся в `<body>`.
|
||||
|
||||
Сборка сделана исключительно для более компактного представления задач, без рекомендаций использовать именно её в проектах.
|
|
@ -1,38 +0,0 @@
|
|||
Новый тест может быть, к примеру, таким:
|
||||
|
||||
```js
|
||||
it("любое число в степени 0 равно 1", function() {
|
||||
assert.equal(pow(123, 0), 1);
|
||||
});
|
||||
```
|
||||
|
||||
Конечно, желательно проверить на нескольких числах.
|
||||
|
||||
Поэтому лучше будет создать блок `describe`, аналогичный тому, что мы делали для произвольных чисел:
|
||||
|
||||
```js
|
||||
describe("любое число, кроме нуля, в степени 0 равно 1", function() {
|
||||
|
||||
function makeTest(x) {
|
||||
it("при возведении " + x + " в степень 0 результат: 1", function() {
|
||||
assert.equal(pow(x, 0), 1);
|
||||
});
|
||||
}
|
||||
|
||||
for (var x = -5; x <= 5; x += 2) {
|
||||
makeTest(x);
|
||||
}
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
И не забудем добавить отдельный тест для нуля:
|
||||
|
||||
```js no-beautify
|
||||
...
|
||||
it("ноль в нулевой степени даёт NaN", function() {
|
||||
assert( isNaN(pow(0, 0)), "0 в степени 0 не NaN");
|
||||
});
|
||||
...
|
||||
```
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://js.cx/test/libs.js"></script>
|
||||
<script src="test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
|
||||
function pow(x, n) {
|
||||
if (n < 0) return NaN;
|
||||
if (Math.round(n) != n) return NaN;
|
||||
if (n == 0 && x == 0) return NaN;
|
||||
|
||||
var result = 1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,44 +0,0 @@
|
|||
describe("pow", function() {
|
||||
|
||||
describe("возводит x в степень n", function() {
|
||||
|
||||
function makeTest(x) {
|
||||
var expected = x * x * x;
|
||||
it("при возведении " + x + " в степень 3 результат: " + expected, function() {
|
||||
assert.equal(pow(x, 3), expected);
|
||||
});
|
||||
}
|
||||
|
||||
for (var x = 1; x <= 5; x++) {
|
||||
makeTest(x);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it("при возведении в отрицательную степень результат NaN", function() {
|
||||
assert(isNaN(pow(2, -1)), "pow(2, -1) не NaN");
|
||||
});
|
||||
|
||||
it("при возведении в дробную степень результат NaN", function() {
|
||||
assert(isNaN(pow(2, 1.5)), "pow(2, -1.5) не NaN");
|
||||
});
|
||||
|
||||
describe("любое число, кроме нуля, в степени 0 равно 1", function() {
|
||||
|
||||
function makeTest(x) {
|
||||
it("при возведении " + x + " в степень 0 результат: 1", function() {
|
||||
assert.equal(pow(x, 0), 1);
|
||||
});
|
||||
}
|
||||
|
||||
for (var x = -5; x <= 5; x += 2) {
|
||||
makeTest(x);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it("ноль в нулевой степени даёт NaN", function() {
|
||||
assert(isNaN(pow(0, 0)), "0 в степени 0 не NaN");
|
||||
});
|
||||
|
||||
});
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://js.cx/test/libs.js"></script>
|
||||
<script src="test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
|
||||
function pow(x, n) {
|
||||
if (n < 0) return NaN;
|
||||
if (Math.round(n) != n) return NaN;
|
||||
|
||||
var result = 1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
result *= x;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,26 +0,0 @@
|
|||
describe("pow", function() {
|
||||
|
||||
describe("возводит x в степень n", function() {
|
||||
|
||||
function makeTest(x) {
|
||||
var expected = x * x * x;
|
||||
it("при возведении " + x + " в степень 3 результат: " + expected, function() {
|
||||
assert.equal(pow(x, 3), expected);
|
||||
});
|
||||
}
|
||||
|
||||
for (var x = 1; x <= 5; x++) {
|
||||
makeTest(x);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it("при возведении в отрицательную степень результат NaN", function() {
|
||||
assert(isNaN(pow(2, -1)), "pow(2, -1) не NaN");
|
||||
});
|
||||
|
||||
it("при возведении в дробную степень результат NaN", function() {
|
||||
assert(isNaN(pow(2, 1.5)), "pow(2, -1.5) не NaN");
|
||||
});
|
||||
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Добавьте тест к задаче
|
||||
|
||||
Добавьте к [предыдущей задаче](/task/pow-nan-spec) тесты, которые будут проверять, что любое число, кроме нуля, в нулевой степени равно `1`, а ноль в нулевой степени даёт `NaN` (это математически корректно, результат 0<sup>0</sup> не определён).
|
||||
|
||||
При необходимости, исправьте реализацию, чтобы тесты проходили без ошибок.
|
|
@ -4,28 +4,47 @@ What we have here is actually 3 tests, but layed out as a single function with 3
|
|||
|
||||
Sometimes it's easier to write this way, but if an error occurs, it's much less obvious what went wrong.
|
||||
|
||||
If an error happens inside a complex execution flow, then we'll have to figure out what was the data at that point.
|
||||
If an error happens inside a complex execution flow, then we'll have to figure out the data at that point. We'll actually have to *debug the test*.
|
||||
|
||||
TODO
|
||||
|
||||
Если в сложном тесте произошла ошибка где-то посередине потока вычислений, то придётся выяснять, какие конкретно были входные и выходные данные на этот момент, то есть по сути -- отлаживать код самого теста.
|
||||
|
||||
Гораздо лучше будет разбить тест на несколько блоков `it`, с чётко прописанными входными и выходными данными.
|
||||
It would be much better to break the test into multiple `it` blocks with clearly written inputs and outputs.
|
||||
|
||||
Like this:
|
||||
```js
|
||||
describe("Возводит x в степень n", function() {
|
||||
it("5 в степени 1 равно 5", function() {
|
||||
describe("Raises x to power n", function() {
|
||||
it("5 in the power of 1 equals 5", function() {
|
||||
assert.equal(pow(5, 1), 5);
|
||||
});
|
||||
|
||||
it("5 в степени 2 равно 25", function() {
|
||||
it("5 in the power of 2 equals 25", function() {
|
||||
assert.equal(pow(5, 2), 25);
|
||||
});
|
||||
|
||||
it("5 в степени 3 равно 125", function() {
|
||||
it("5 in the power of 3 equals 125", function() {
|
||||
assert.equal(pow(5, 3), 125);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Можно использовать цикл для генерации блоков `it`, в этом случае важно, чтобы сам код такого цикла был достаточно простым. Иногда проще записать несколько блоков `it` вручную, как сделано выше, чем "городить огород" из синтаксических конструкций.
|
||||
We replaced the single `it` with `describe` and a group of `it` blocks. Now if something fails we would see clearly what the data was.
|
||||
|
||||
Also we can isolate a single test and run it in standalone mode by writing `it.only` instead of `it`:
|
||||
|
||||
|
||||
```js
|
||||
describe("Raises x to power n", function() {
|
||||
it("5 in the power of 1 equals 5", function() {
|
||||
assert.equal(pow(5, 1), 5);
|
||||
});
|
||||
|
||||
*!*
|
||||
// Mocha will run only this block
|
||||
it.only("5 in the power of 2 equals 25", function() {
|
||||
assert.equal(pow(5, 2), 25);
|
||||
});
|
||||
*/!*
|
||||
|
||||
it("5 in the power of 3 equals 125", function() {
|
||||
assert.equal(pow(5, 3), 125);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
|
|
@ -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/11-modifying-document/12-sort-table/task.md
Normal file
39
2-ui/1-document/11-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.
|
|
@ -49,7 +49,6 @@
|
|||
|
||||
// добавить кол-во детей к текстовому узлу
|
||||
li.firstChild.data += ' [' + childCount + ']';
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ importance: 5
|
|||
|
||||
---
|
||||
|
||||
# Дерево
|
||||
# Show descendants in a tree
|
||||
|
||||
There's a tree organized as nested `ul/li`.
|
||||
|
||||
|
|
|
@ -394,6 +394,54 @@ Let's make our message to disappear after a second:
|
|||
</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:
|
||||
|
@ -429,3 +477,8 @@ Insertion and removal of nodes:
|
|||
- `"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.
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
Есть два варианта.
|
||||
|
||||
1. Можно использовать свойство `elem.style.cssText` и присвоить стиль в текстовом виде. При этом все присвоенные ранее свойства `elem.style` будут удалены.
|
||||
2. Можно назначить подсвойства `elem.style` одно за другим. Этот способ более безопасен, т.к. меняет только явно присваемые свойства.
|
||||
|
||||
Мы выберем второй путь.
|
||||
|
||||
**Описание CSS-свойств:**
|
||||
|
||||
```css
|
||||
.button {
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
border: 2px groove green;
|
||||
display: block;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
width: 100px;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
```
|
||||
|
||||
`*-border-radius`
|
||||
: Добавляет скругленные углы. Свойство присваивается в вариантах для Firefox `-moz-...`, Chrome/Safari `-webkit-...` и стандартное CSS3-свойство для тех, кто его поддерживает (Opera).
|
||||
|
||||
`display`
|
||||
: По умолчанию, у `A` это свойство имеет значение `display: inline`.
|
||||
|
||||
`height`, `line-height`
|
||||
: Устанавливает высоту и делает текст вертикально центрированным путем установки `line-height` в значение, равное высоте. Такой способ центрирования текста работает, если он состоит из одной строки.
|
||||
|
||||
`text-align`
|
||||
: Центрирует текст горизонтально.
|
||||
|
||||
`color`, `font-weight`
|
||||
: Делает текст красным и жирным.
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div>
|
||||
Кнопка:
|
||||
<!-- создайте элемент и расположите его тут -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var a = document.createElement('a');
|
||||
a.className = 'button';
|
||||
a.appendChild(document.createTextNode('Нажми меня'));
|
||||
a.href = '/';
|
||||
|
||||
var s = a.style
|
||||
s.MozBorderRadius = s.WebkitBorderRadius = s.borderRadius = '8px';
|
||||
s.border = '2px groove green';
|
||||
s.display = 'block';
|
||||
s.height = '30px';
|
||||
s.lineHeight = '30px';
|
||||
s.width = '100px';
|
||||
s.textDecoration = 'none';
|
||||
s.textAlign = 'center';
|
||||
s.color = 'red';
|
||||
s.fontWeight = 'bold';
|
||||
|
||||
var div = document.body.children[0];
|
||||
div.appendChild(a);
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div>
|
||||
Кнопка:
|
||||
<!-- создайте элемент и расположите его тут -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// .. ваш код
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
importance: 3
|
||||
|
||||
---
|
||||
|
||||
# Скругленая кнопка со стилями из JavaScript
|
||||
|
||||
Создайте кнопку в виде элемента `<a>` с заданным стилем, используя JavaScript.
|
||||
|
||||
В примере ниже такая кнопка создана при помощи HTML/CSS. В вашем решении кнопка должна создаваться, настраиваться и добавляться в документ при помощи *только JavaScript*, без тегов `<style>` и `<a>`.
|
||||
|
||||
```html autorun height="50"
|
||||
<style>
|
||||
.button {
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
border: 2px groove green;
|
||||
display: block;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
width: 100px;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<a class="button" href="/">Нажми меня</a>
|
||||
```
|
||||
|
||||
**Проверьте себя: вспомните, что означает каждое свойство. В чём состоит эффект его появления здесь?**
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
.notification {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
font: normal 20px Georgia;
|
||||
background: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
background: red;
|
||||
color: yellow;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h2>Уведомление</h2>
|
||||
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum
|
||||
magnam consequatur sit laboriosam velit excepturi laborum sequi eos placeat et quia deleniti? Corrupti velit impedit autem et obcaecati fuga debitis nemo ratione iste veniam amet dicta hic ipsam unde cupiditate incidunt aut iure ipsum officiis soluta
|
||||
temporibus. Tempore dicta ullam delectus numquam consectetur quisquam explicabo culpa excepturi placeat quo sequi molestias reprehenderit hic at nemo cumque voluptates quidem repellendus maiores unde earum molestiae ad.
|
||||
</p>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Показывает уведомление, пропадающее через 1.5 сек
|
||||
*
|
||||
* @param options.top {number} вертикальный отступ, в px
|
||||
* @param options.right {number} правый отступ, в px
|
||||
* @param options.cssText {string} строка стиля
|
||||
* @param options.className {string} CSS-класс
|
||||
* @param options.html {string} HTML-текст для показа
|
||||
*/
|
||||
function showNotification(options) {
|
||||
|
||||
var notification = document.createElement('div');
|
||||
notification.className = "notification";
|
||||
if (options.cssText) {
|
||||
notification.style.cssText = options.cssText;
|
||||
}
|
||||
notification.style.top = (options.top || 0) + 'px'; // can use cssText
|
||||
notification.style.right = (options.right || 0) + 'px'; // can use cssText
|
||||
if (options.className) {
|
||||
notification.classList.add(options.className);
|
||||
}
|
||||
|
||||
notification.innerHTML = options.html;
|
||||
document.body.appendChild(notification); // over cover
|
||||
|
||||
setTimeout(function() {
|
||||
document.body.removeChild(notification);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// тест работоспособности
|
||||
var i = 0;
|
||||
setInterval(function() {
|
||||
showNotification({
|
||||
top: 10,
|
||||
right: 10,
|
||||
html: 'Привет ' + ++i,
|
||||
className: "welcome"
|
||||
});
|
||||
}, 2000);
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
.notification {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
padding: 5px;
|
||||
border: 1px solid black;
|
||||
font: normal 20px Georgia;
|
||||
background: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
background: red;
|
||||
color: yellow;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h2>Уведомление</h2>
|
||||
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum
|
||||
magnam consequatur sit laboriosam velit excepturi laborum sequi eos placeat et quia deleniti? Corrupti velit impedit autem et obcaecati fuga debitis nemo ratione iste veniam amet dicta hic ipsam unde cupiditate incidunt aut iure ipsum officiis soluta
|
||||
temporibus. Tempore dicta ullam delectus numquam consectetur quisquam explicabo culpa excepturi placeat quo sequi molestias reprehenderit hic at nemo cumque voluptates quidem repellendus maiores unde earum molestiae ad.
|
||||
</p>
|
||||
|
||||
<p>В CSS есть готовый класс notification, который можно ставить уведомлению.</p>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Показывает уведомление, пропадающее через 1.5 сек
|
||||
*
|
||||
* @param options.top {number} вертикальный отступ, в px
|
||||
* @param options.right {number} правый отступ, в px
|
||||
* @param options.cssText {string} строка стиля
|
||||
* @param options.className {string} CSS-класс
|
||||
* @param options.html {string} HTML-текст для показа
|
||||
*/
|
||||
function showNotification(options) {
|
||||
// ваш код
|
||||
}
|
||||
|
||||
// тест работоспособности
|
||||
var i = 0;
|
||||
setInterval(function() {
|
||||
showNotification({
|
||||
top: 10,
|
||||
right: 10,
|
||||
html: 'Привет ' + ++i,
|
||||
className: "welcome"
|
||||
});
|
||||
}, 2000);
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,41 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Создать уведомление
|
||||
|
||||
Напишите функцию `showNotification(options)`, которая показывает уведомление, пропадающее через 1.5 сек.
|
||||
|
||||
Описание функции:
|
||||
|
||||
```js
|
||||
/**
|
||||
* Показывает уведомление, пропадающее через 1.5 сек
|
||||
*
|
||||
* @param options.top {number} вертикальный отступ, в px
|
||||
* @param options.right {number} правый отступ, в px
|
||||
* @param options.cssText {string} строка стиля
|
||||
* @param options.className {string} CSS-класс
|
||||
* @param options.html {string} HTML-текст для показа
|
||||
*/
|
||||
function showNotification(options) {
|
||||
// ваш код
|
||||
}
|
||||
```
|
||||
|
||||
Пример использования:
|
||||
|
||||
```js
|
||||
// покажет элемент с текстом "Привет" и классом welcome справа-сверху окна
|
||||
showNotification({
|
||||
top: 10,
|
||||
right: 10,
|
||||
html: "Привет",
|
||||
className: "welcome"
|
||||
});
|
||||
```
|
||||
|
||||
[demo src="solution"]
|
||||
|
||||
Элемент уведомления должен иметь CSS-класс `notification`, к которому добавляется класс из `options.className`, если есть. Исходный документ содержит готовые стили.
|
||||
|
295
2-ui/1-document/14-styles-and-classes/article.md
Normal file
295
2-ui/1-document/14-styles-and-classes/article.md
Normal file
|
@ -0,0 +1,295 @@
|
|||
# Styles and classes
|
||||
|
||||
Before we get to Javascript ways of dealing with styles and classes -- here's an important rule.
|
||||
|
||||
There are generally two ways to style an element, both in HTML and Javascript:
|
||||
|
||||
1. Create a class in CSS and add it: `<div class="...">`
|
||||
2. Write properties directly into `style`: `<div style="...">`.
|
||||
|
||||
[cut]
|
||||
|
||||
CSS is always the preferred way, both in HTML and Javascript. We should only use `style` if classes "can't handle it".
|
||||
|
||||
For instance, `style` is acceptable if we calculated coordinates for an element dynamically and want to set them from Javascript, like here:
|
||||
|
||||
```js
|
||||
let top = /* complex calculations */;
|
||||
let left = /* complex calculations */;
|
||||
elem.style.left = left; // e.g '123px'
|
||||
elem.style.top = top; // e.g '456px'
|
||||
```
|
||||
|
||||
For other cases, like making the text red, adding a background icon -- describe that in CSS and then apply the class. That's more flexible and easier to support.
|
||||
|
||||
## className and classList
|
||||
|
||||
Changing a class is one of the most often actions in scripts.
|
||||
|
||||
In the ancient time, there was a limitation in Javascript: a reserved word like `"class"` could not be an object property. That limitation does not exist now, but at that time it was impossible to use `elem.class`.
|
||||
|
||||
So instead of `elem.class` we have `elem.className` property. It's the string with all classes, the same value as in the `"class"` attribute.
|
||||
|
||||
For instance:
|
||||
|
||||
```html run
|
||||
<body class="main page">
|
||||
<script>
|
||||
alert(document.body.className); // main page
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Adding/removing a class is a widespread operation. Using a string for such purpose is cumbersome, so there's another property for that: `elem.classList`.
|
||||
|
||||
The `elem.classList` is a special object with methods to `add/remove/toggle` classes.
|
||||
|
||||
For instance:
|
||||
|
||||
```html run
|
||||
<body class="main page">
|
||||
<script>
|
||||
document.body.classList.add('article');
|
||||
alert(document.body.className); // main page article
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
So we can operate both on the full class string using `className` or on individual classes using `classList`. What we choose depends on our needs.
|
||||
|
||||
Methods of `classList`:
|
||||
|
||||
- `elem.classList.add/remove("class")` -- adds/removes the class.
|
||||
- `elem.classList.toggle("class")` -- if the class exists, then removes it, otherwise adds it.
|
||||
- `elem.classList.contains("class")` -- returns `true/false`, checks for the given class.
|
||||
|
||||
Besides that, `classList` is iterable, so we can list all classes like this:
|
||||
|
||||
```html run
|
||||
<body class="main page">
|
||||
<script>
|
||||
for(let name of document.body.classList) {
|
||||
alert(name); // main, and then page
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
## Element style
|
||||
|
||||
The property `elem.style` is an object that corresponds to what's written in the `"style"` attribute. Setting `elem.style.width="100px"` works as if we had in the attribute `style="width:100px"`.
|
||||
|
||||
For multi-word property the camelCase is used:
|
||||
|
||||
```js no-beautify
|
||||
background-color => elem.style.backgroundColor
|
||||
z-index => elem.style.zIndex
|
||||
border-left-width => elem.style.borderLeftWidth
|
||||
```
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
document.body.style.backgroundColor = prompt('background color?', 'green');
|
||||
```
|
||||
|
||||
````smart header="Prefixed properties"
|
||||
Browser-prefixed properties like `-moz-border-radius`, `-webkit-border-radius` also follow the same rule, for instance:
|
||||
|
||||
```js
|
||||
button.style.MozBorderRadius = '5px';
|
||||
button.style.WebkitBorderRadius = '5px';
|
||||
```
|
||||
|
||||
That is: a dash `"-"` becomes an uppercase.
|
||||
````
|
||||
|
||||
## Resetting the style
|
||||
|
||||
To "reset" the style property, we should assign an empty line to it. For instance, if we set a `width` and now want to remove it, then `elem.style.width=""`.
|
||||
|
||||
For instance, to hide an element, we can set `elem.style.display = "none"`.
|
||||
|
||||
And to show it back, we should not set another `display` like `elem.style.display = "block"`. To return the "default" `display`: `elem.style.display = ""`.
|
||||
|
||||
```js run
|
||||
// if we run this code, the <body> would "blink"
|
||||
document.body.style.display = "none";
|
||||
|
||||
setTimeout(() => document.body.style.display = "", 1000);
|
||||
```
|
||||
|
||||
If we set `display` to an empty string, then the browser applies CSS classes and its built-in styles normally, as if there were no such `style` property.
|
||||
|
||||
````smart header="Full rewrite with `style.cssText`"
|
||||
Normally, `style.*` assign individual style properties. We can't set the full style like `div.style="color: red; width: 100px"`, because `div.style` is an object.
|
||||
|
||||
To set the full style as a string, there's a special property `style.cssText`:
|
||||
|
||||
```html run
|
||||
<div id="div">Button</div>
|
||||
|
||||
<script>
|
||||
// we can set special style flags like "important" here
|
||||
div.style.cssText=`color: red !important;
|
||||
background-color: yellow;
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
alert(div.style.cssText);
|
||||
</script>
|
||||
```
|
||||
|
||||
We rarely use it, because such a setting removes all existing styles: not adds, but rather replaces them. But still can be done for new elements when we know we don't delete something important.
|
||||
|
||||
The same can be accomplished by setting an attribute: `div.setAttribute('style', "color: red...")`.
|
||||
````
|
||||
|
||||
## Mind the units
|
||||
|
||||
CSS units must exist in values. We should not set `elem.style.top` to `10`, but rather to `10px`. Otherwise it wouldn't work.
|
||||
|
||||
For instance:
|
||||
|
||||
```html run height=100
|
||||
<body>
|
||||
<script>
|
||||
*!*
|
||||
// won't work!
|
||||
document.body.style.margin = 20;
|
||||
alert(document.body.style.margin); // '' (empty string)
|
||||
*/!*
|
||||
|
||||
// now the right way
|
||||
document.body.style.margin = '20px';
|
||||
alert(document.body.style.margin); // 20px
|
||||
|
||||
alert(document.body.style.marginTop); // 20px
|
||||
alert(document.body.style.marginLeft); // 20px
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Please note how the browser "unpacks" the property `style.margin` and infers `style.marginLeft` and `style.marginTop` (and other partial margins) from it.
|
||||
|
||||
## Computed styles: getComputedStyle
|
||||
|
||||
Modifying a style is easy. But how to *read* it?
|
||||
|
||||
For instance, we want to know the size, margins, the color of an element. How to do it?
|
||||
|
||||
**The `style` property contains only the style in the `"style"` attribute, without any CSS cascade.**
|
||||
|
||||
So we can't read anything that comes from CSS classes.
|
||||
|
||||
For instance, here `style` won't see the margin:
|
||||
|
||||
```html run height=60 no-beautify
|
||||
<head>
|
||||
<style> body { color: red; margin: 5px } </style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
The red text
|
||||
<script>
|
||||
*!*
|
||||
alert(document.body.style.color); // empty
|
||||
alert(document.body.style.marginTop); // empty
|
||||
*/!*
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
...But what if we need, say, increase the margin by 20px? We want the current value for that.
|
||||
|
||||
There's another method for that: `getComputedStyle`.
|
||||
|
||||
The syntax is:
|
||||
|
||||
```js
|
||||
getComputedStyle(element[, pseudo])
|
||||
```
|
||||
|
||||
element
|
||||
: Element to read the value for.
|
||||
|
||||
pseudo
|
||||
: A pseudo-element if required, for instance `::before`. An empty string or no argument mean the element itself.
|
||||
|
||||
The result is an object with style properties, like `elem.style`, but now with respect to all CSS classes.
|
||||
|
||||
For instance:
|
||||
|
||||
```html run height=100
|
||||
<head>
|
||||
<style> body { color: red; margin: 5px } </style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
let computedStyle = getComputedStyle(document.body);
|
||||
|
||||
// now can read the margin and the color from it
|
||||
|
||||
alert( computedStyle.marginTop ); // 5px
|
||||
alert( computedStyle.color ); // rgb(255, 0, 0)
|
||||
</script>
|
||||
|
||||
</body>
|
||||
```
|
||||
|
||||
```smart header="Computed and resolved values"
|
||||
There are two concepts in [CSS](https://drafts.csswg.org/cssom/#resolved-values):
|
||||
|
||||
1. A *computed* style value is the one after all CSS rules and CSS inheritance is applied. If can look like `width: auto` or `font-size: 125%`.
|
||||
2. A *resolved* style value is the one finally applied to the element. The browser takes the computed value and makes all units fixed and absolute, for instance: `width: 212px` or `font-size: 16px`. In some browsers values can have a floating point.
|
||||
|
||||
Long time ago `getComputedStyle` was created to get computed values, but it turned out that resolved values are much more convenient.
|
||||
|
||||
So nowadays `getComputedStyle` actually returns the final, resolved value.
|
||||
```
|
||||
|
||||
````warn header="`getComputedStyle` requires the full property name"
|
||||
We should always ask for the exact property that we want, like `paddingLeft` or `marginTop` or `borderTopWidth`. Otherwise the correct result is not guaranteed.
|
||||
|
||||
For instance, if properties `paddingLeft/paddingTop` come from the different CSS classes, then what should we get for `getComputedStyle(elem).padding`?
|
||||
|
||||
Some browsers (Chrome) show `10px` in the document below, and some of them (Firefox) -- do not:
|
||||
|
||||
```html run
|
||||
<style>
|
||||
body {
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
let style = getComputedStyle(document.body);
|
||||
alert(style.margin); // empty string in Firefox
|
||||
</script>
|
||||
```
|
||||
````
|
||||
|
||||
```smart header="\"Visited\" links styles are hidden!"
|
||||
Visited links may be colored using `:visited` CSS pseudoclass.
|
||||
|
||||
But `getComputedStyle` does not give access to that color, because otherwise an arbitrary page could find out whether the user visited a link by creating it on the page and checking the styles.
|
||||
|
||||
Javascript we may not see the styles applied by `:visited`. And also, there's a limitation in CSS that forbids to apply geometry-changing styles in `:visited`. That's to guarantee that there's no side way for an evil page to see if a link was visited and hence to break the privacy.
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
To manage classes, there are two DOM properties:
|
||||
- `className` -- the string value, good to manage the whole set of classes.
|
||||
- `classList` -- the object with methods `add/remove/toggle/contains`, good for individual classes.
|
||||
|
||||
To change the styles:
|
||||
|
||||
- The `style` property is an object with camelCased styles. Reading and writing to it has the same meaning as modifying individual properties in the `"style"` attribute. To see how to apply `important` and other rare stuff -- there's a list of methods at [MDN](mdn:api/CSSStyleDeclaration).
|
||||
|
||||
- The `style.cssText` property corresponds to the whole `"style"` attribute, the full string of styles.
|
||||
|
||||
To read the resolved styles (after all CSS is applied and final values are calculated):
|
||||
|
||||
- The `getComputedStyles(elem[, pseudo])` returns the style-like object with them. Read-only.
|
|
@ -0,0 +1,18 @@
|
|||
function getIEComputedStyle(elem, prop) {
|
||||
var value = elem.currentStyle[prop] || 0;
|
||||
|
||||
// we use 'left' property as a place holder so backup values
|
||||
var leftCopy = elem.style.left;
|
||||
var runtimeLeftCopy = elem.runtimeStyle.left;
|
||||
|
||||
// assign to runtimeStyle and get pixel value
|
||||
elem.runtimeStyle.left = elem.currentStyle.left;
|
||||
elem.style.left = (prop === "fontSize") ? "1em" : value;
|
||||
value = elem.style.pixelLeft + "px";
|
||||
|
||||
// restore values for left
|
||||
elem.style.left = leftCopy;
|
||||
elem.runtimeStyle.left = runtimeLeftCopy;
|
||||
|
||||
return value;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script src="getiecomputedstyle.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<style>
|
||||
#margin-test {
|
||||
margin: 1%;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="margin-test">Тестовый элемент с margin 1%</div>
|
||||
|
||||
<script>
|
||||
var elem = document.getElementById('margin-test');
|
||||
if (!window.getComputedStyle) { // старые IE
|
||||
document.write(getIEComputedStyle(elem, 'marginTop'));
|
||||
} else {
|
||||
document.write('Пример работает только в IE8-');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,18 +1,19 @@
|
|||
Сделаем цикл по узлам `<li>`:
|
||||
Let's make a loop over `<li>`:
|
||||
|
||||
```js
|
||||
var lis = document.getElementsByTagName('li');
|
||||
|
||||
for (i = 0; i < lis.length; i++) {
|
||||
for (let li of document.querySelector('li')) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
В цикле для каждого `lis[i]` можно получить текст, используя свойство `firstChild`. Ведь первым в `<li>` является как раз текстовый узел, содержащий текст названия.
|
||||
In the loop we need to get the text inside every `li`. We can read it directly from the first child node, that is the text node:
|
||||
|
||||
Также можно получить количество потомков, используя `lis[i].getElementsByTagName('li')`.
|
||||
```js
|
||||
for (let li of document.querySelector('li')) {
|
||||
let title = li.firstChild.data;
|
||||
|
||||
Напишите код с этой подсказкой.
|
||||
|
||||
Если уж не выйдет -- тогда откройте решение.
|
||||
// title is the text in <li> before any other nodes
|
||||
}
|
||||
```
|
||||
|
||||
Then we can get the number of descendants `li.getElementsByTagName('li')`.
|
||||
|
|
|
@ -1,44 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
<li>Животные
|
||||
<li>Animals
|
||||
<ul>
|
||||
<li>Млекопитающие
|
||||
<li>Mammals
|
||||
<ul>
|
||||
<li>Коровы</li>
|
||||
<li>Ослы</li>
|
||||
<li>Собаки</li>
|
||||
<li>Тигры</li>
|
||||
<li>Cows</li>
|
||||
<li>Donkeys</li>
|
||||
<li>Dogs</li>
|
||||
<li>Tigers</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Другие
|
||||
<li>Other
|
||||
<ul>
|
||||
<li>Змеи</li>
|
||||
<li>Птицы</li>
|
||||
<li>Ящерицы</li>
|
||||
<li>Snakes</li>
|
||||
<li>Birds</li>
|
||||
<li>Lizards</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Рыбы
|
||||
<li>Fishes
|
||||
<ul>
|
||||
<li>Аквариумные
|
||||
<li>Aquarium
|
||||
<ul>
|
||||
<li>Гуппи</li>
|
||||
<li>Скалярии</li>
|
||||
<li>Guppy</li>
|
||||
<li>Angelfish</li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
<li>Морские
|
||||
<li>Sea
|
||||
<ul>
|
||||
<li>Морская форель</li>
|
||||
<li>Sea trout</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -46,21 +40,18 @@
|
|||
</ul>
|
||||
|
||||
<script>
|
||||
var lis = document.getElementsByTagName('li');
|
||||
for (let li of document.querySelector('li')) {
|
||||
// get the title from the text node
|
||||
let title = li.firstChild.data;
|
||||
|
||||
for (i = 0; i < lis.length; i++) {
|
||||
// получить название из текстового узла
|
||||
var title = lis[i].firstChild.data;
|
||||
title = title.trim(); // remove extra spaces from ends
|
||||
|
||||
title = title.trim(); // убрать лишние пробелы с концов
|
||||
// get the descendants count
|
||||
let count = li.getElementsByTagName('li').length;
|
||||
|
||||
// получить количество детей
|
||||
var childCount = lis[i].getElementsByTagName('li').length;
|
||||
|
||||
alert(title + ': ' + childCount);
|
||||
alert(title + ': ' + count);
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,44 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
<li>Животные
|
||||
<li>Animals
|
||||
<ul>
|
||||
<li>Млекопитающие
|
||||
<li>Mammals
|
||||
<ul>
|
||||
<li>Коровы</li>
|
||||
<li>Ослы</li>
|
||||
<li>Собаки</li>
|
||||
<li>Тигры</li>
|
||||
<li>Cows</li>
|
||||
<li>Donkeys</li>
|
||||
<li>Dogs</li>
|
||||
<li>Tigers</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Другие
|
||||
<li>Other
|
||||
<ul>
|
||||
<li>Змеи</li>
|
||||
<li>Птицы</li>
|
||||
<li>Ящерицы</li>
|
||||
<li>Snakes</li>
|
||||
<li>Birds</li>
|
||||
<li>Lizards</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Рыбы
|
||||
<li>Fishes
|
||||
<ul>
|
||||
<li>Аквариумные
|
||||
<li>Aquarium
|
||||
<ul>
|
||||
<li>Гуппи</li>
|
||||
<li>Скалярии</li>
|
||||
<li>Guppy</li>
|
||||
<li>Angelfish</li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
<li>Морские
|
||||
<li>Sea
|
||||
<ul>
|
||||
<li>Морская форель</li>
|
||||
<li>Sea trout</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -46,9 +40,8 @@
|
|||
</ul>
|
||||
|
||||
<script>
|
||||
// .. ваш код ..
|
||||
// ... your code...
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -2,14 +2,13 @@ importance: 5
|
|||
|
||||
---
|
||||
|
||||
# Дерево
|
||||
# Count descendants
|
||||
|
||||
Есть дерево из тегов `<ul>/<li>`.
|
||||
There's a tree structured as nested `ul/li`.
|
||||
|
||||
Напишите код, который для каждого элемента `<li>` выведет:
|
||||
Write the code that for each `<li>` shows:
|
||||
|
||||
1. Текст непосредственно в нём (без подразделов).
|
||||
2. Количество вложенных в него элементов `<li>` -- всех, с учётом вложенных.
|
||||
1. What's the text inside it (without the subtree)
|
||||
2. The number of nested `<li>` -- all descendants, including the deeply nested ones.
|
||||
|
||||
[demo src="solution"]
|
||||
|
||||
|
|
|
@ -215,27 +215,20 @@ In other words, the method `closest` goes up from the element and checks each of
|
|||
For instance:
|
||||
|
||||
```html run
|
||||
<ul>
|
||||
<li class="chapter">Chapter I
|
||||
<ul>
|
||||
<li class="subchapter">Chapter <span class="num">1.1</span></li>
|
||||
<li class="subchapter">Chapter <span class="num">1.2</span></li>
|
||||
</ul>
|
||||
</li>
|
||||
<h1>Contents</h1>
|
||||
|
||||
<ul class="book">
|
||||
<li class="chapter">Chapter 1</li>
|
||||
<li class="chapter">Chapter 1</li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
let numSpan = document.querySelector('.num');
|
||||
let chapter = document.querySelector('.chapter'); // LI
|
||||
|
||||
// nearest li
|
||||
alert(numSpan.closest('li').className) // subchapter
|
||||
alert(chapter.closest('.book')); // UL above our this LI
|
||||
|
||||
// nearest chapter
|
||||
alert(numSpan.closest('.chapter').className) // chapter
|
||||
|
||||
// nearest span
|
||||
// (the numSpan is the span itself, so it's the result)
|
||||
alert(numSpan.closest('span') === numSpan) // true
|
||||
alert(chapter.closest('h1')); // null
|
||||
// because h1 is not an ancestor
|
||||
</script>
|
||||
```
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ Write the code to select the element with `data-widget-name` attribute from the
|
|||
<html>
|
||||
<body>
|
||||
|
||||
<div data-widget-name="menu">Выберите жанр</div>
|
||||
<div data-widget-name="menu">Choose the genre</div>
|
||||
|
||||
<script>
|
||||
/* your code */
|
||||
|
|
|
@ -18,7 +18,7 @@ for (let link of links) {
|
|||
|
||||
if (href.startsWith('http://internal.com')) continue; // internal
|
||||
|
||||
link.classList.add('external');
|
||||
link.style.color = 'yellow';
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -32,5 +32,5 @@ Please note: we use `link.getAttribute('href')`. Not `link.href`, because we nee
|
|||
let selector = 'a[href*="://"]:not([href^="http://internal.com"])';
|
||||
let links = document.querySelectorAll(selector);
|
||||
|
||||
links.forEach(link => link.classList.add('external'));
|
||||
links.forEach(link => link.style.color = 'yellow');
|
||||
```
|
|
@ -1,14 +1,5 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.external {
|
||||
background-color: yellow;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a name="list">the list</a>
|
||||
|
@ -25,7 +16,7 @@
|
|||
let selector = 'a[href*="://"]:not([href^="http://internal.com"])';
|
||||
let links = document.querySelectorAll(selector);
|
||||
|
||||
links.forEach(link => link.classList.add('external'))
|
||||
links.forEach(link => link.style.color = 'yellow');
|
||||
</script>
|
||||
|
||||
</body>
|
|
@ -2,21 +2,17 @@ importance: 3
|
|||
|
||||
---
|
||||
|
||||
# Add the class to external links
|
||||
# Make external links yellow
|
||||
|
||||
Make all external links yellow by adding them the class `"external"`.
|
||||
Make all external links yellow by altering their `style` property.
|
||||
|
||||
A link is external if:
|
||||
- It's `href` has `://` in it
|
||||
- But doesn't start with `http://internal.com`.
|
||||
|
||||
```html run
|
||||
<style>
|
||||
.external {
|
||||
background-color: yellow
|
||||
}
|
||||
</style>
|
||||
Example:
|
||||
|
||||
```html run
|
||||
<a name="list">the list</a>
|
||||
<ul>
|
||||
<li><a href="http://google.com">http://google.com</a></li>
|
||||
|
@ -26,6 +22,12 @@ A link is external if:
|
|||
<li><a href="http://nodejs.org">http://nodejs.org</a></li>
|
||||
<li><a href="http://internal.com/test">http://internal.com/test</a></li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
// setting style for a single link
|
||||
let link = document.querySelector('a');
|
||||
link.style.color = 'yellow';
|
||||
</script>
|
||||
```
|
||||
|
||||
The result should be:
|
|
@ -204,7 +204,18 @@ DOM properties are not always strings. For instance, `input.checked` property (f
|
|||
</script>
|
||||
```
|
||||
|
||||
Javascript enforces the right type for a DOM property.
|
||||
There are more advanced examples. As we've already seen, `style` property is an object:
|
||||
|
||||
```html run
|
||||
<div id="div" style="color:red;font-size:120%">Hello</div>
|
||||
|
||||
<script>
|
||||
alert(div.getAttribute('style')); // color:red;font-size:120%
|
||||
|
||||
alert(div.style); // [object CSSStyleDeclaration]
|
||||
alert(div.style.color); // red
|
||||
</script>
|
||||
```
|
||||
|
||||
**But even if a DOM property type is a string, it may differ from the attribute.**
|
||||
|
||||
|
@ -226,50 +237,6 @@ Here we can see that:
|
|||
Let's note again: if we need the value exactly as written in the HTML, we need to use `getAttribute`.
|
||||
|
||||
|
||||
## className and classList
|
||||
|
||||
The `"class"` attribute is truly special, as there are two properties that correspond to it:
|
||||
1. `className` -- the string, same value as in the attribute. In the old times, there was a problem to use a reserved word such as `"class"` as an object property (now it's ok). So `className` was chosen instead.
|
||||
2. `classList` -- a special object with methods to `add/remove/toggle` classes. That's for better convenience.
|
||||
|
||||
For instance:
|
||||
|
||||
```html run
|
||||
<body class="main page">
|
||||
<script>
|
||||
// className: get the classes
|
||||
alert( document.body.className ); // main page
|
||||
|
||||
// className: change the classes
|
||||
document.body.className = "class1 class2";
|
||||
|
||||
// classList: add one more class
|
||||
document.body.classList.add('class3');
|
||||
alert(document.body.className); // class1 class2 class 3
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
So we can operate both on the full class string using `className` or on individual classes using `classList`. What we choose depends on our needs.
|
||||
|
||||
Methods of `classList`:
|
||||
|
||||
- `elem.classList.contains("class")` -- returns `true/false`, checks for the given class.
|
||||
- `elem.classList.add/remove("class")` -- adds/removes the class.
|
||||
- `elem.classList.toggle("class")` -- if the class exists, then removes it, otherwise adds it.
|
||||
|
||||
Besides that, `classList` is iterable, so we can list all classes like this:
|
||||
|
||||
```html run
|
||||
<body class="main page">
|
||||
<script>
|
||||
for(let name of document.body.classList) {
|
||||
alert(name); // main, then page
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
|
||||
## Non-standard attributes, dataset
|
||||
|
||||
When writing HTML, we use a lot of standard attributes. But what about non-standard, custom ones? May they be useful? What for?
|
||||
|
@ -333,15 +300,7 @@ That's because an attribute is more convenient to manage. If we want to change t
|
|||
div.setAttribute('order-state', 'canceled');
|
||||
```
|
||||
|
||||
For classes we would have to clean the current state and add the new one. More cumbersome:
|
||||
|
||||
```js
|
||||
// two lines, and we need to know the old class to clean it
|
||||
div.classList.remove('order-state-new');
|
||||
div.classList.add('order-state-canceled');
|
||||
```
|
||||
|
||||
...But there's a problem here. What if we use a non-standard attribute for our purposes and later the standard introduces it and makes it do something? The HTML language is alive, it grows, more attributes appear to suit the needs of developers. There may be unexpected side-effects.
|
||||
...But there may be a possible problem here. What if we use a non-standard attribute for our purposes and later the standard introduces it and makes it do something? The HTML language is alive, it grows, more attributes appear to suit the needs of developers. There may be unexpected side-effects.
|
||||
|
||||
To evade conflicts, there exist [data-*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes) attributes.
|
||||
|
||||
|
@ -432,7 +391,3 @@ Some use cases:
|
|||
|
||||
- We want to access a non-standard attribute. But if it starts with `data-`, then we should use `dataset`.
|
||||
- We want to read the value "as written" in HTML. The value of the DOM property may be different, for instance `href` property is always a full URL, and we may want to get the "original" value.
|
||||
|
||||
Also please note that for `class` attribute there are two DOM properties:
|
||||
- `className` -- the string value.
|
||||
- `classList` -- the object for easier management of individual classes.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue