Что выведет этот код?
<script>
var body = document.body;
body.innerHTML = "<!--" + body.tagName + "-->";
alert(body.firstChild.data); // что выведет?
</script>
Ответ: BODY.
<script>
var body = document.body;
body.innerHTML = "<!--" + body.tagName + "-->";
alert(body.firstChild.data); // BODY
</script>
Происходящее по шагам:
<!--BODY-->
, так как body.tagName == "BODY"
. Как мы помним, свойство tagName
в HTML всегда находится в верхнем регистре.body.firstChild
.data
для комментария body.firstChild
. Оно равно содержимому узла для всех узлов, кроме элементов. Содержимое комментария: "BODY".Напишите функцию, которая удаляет элемент из DOM.
Синтаксис должен быть таким: remove(elem), то есть, в отличие от parentNode.removeChild(elem)
— без родительского элемента.
<div>Это</div>
<div>Все</div>
<div>Элементы DOM</div>
<script>
var elem = document.body.children[0];
function remove(elem) { /* ваш код */ }
remove(elem); // <-- функция должна удалить элемент
</script>
Родителя parentNode
можно получить из elem
.
Нужно учесть два момента.
Для совместимости со стандартным методом нужно вернуть удаленный элемент. Вот так выглядит решение:
function remove(elem) {
return elem.parentNode ? elem.parentNode.removeChild(elem) : elem;
}
Напишите функцию insertAfter(elem, refElem), которая добавит elem после узла refElem.
<div>Это</div>
<div>Элементы</div>
<script>
var elem = document.createElement('div');
elem.innerHTML = '<b>Новый элемент</b>';
function insertAfter(elem, refElem) { /* ваш код */ }
var body = document.body;
// вставить elem после первого элемента
insertAfter(elem, body.firstChild); // <--- должно работать
// вставить elem за последним элементом
insertAfter(elem, body.lastChild); // <--- должно работать
</script>
Для того, чтобы добавить элемент после refElem
, мы можем вставить его перед refElem.nextSibling
.
Но что если nextSibling
нет? Это означает, что refElem
является последним потомком своего родителя и можем использовать appendChild
.
Код:
function insertAfter(elem, refElem) {
var parent = refElem.parentNode;
var next = refElem.nextSibling;
if (next) {
return parent.insertBefore(elem, next);
} else {
return parent.appendChild(elem);
}
}
Но код может быть гораздо короче, если использовать фишку со вторым аргументом null метода insertBefore
:
function insertAfter(elem, refElem) {
return refElem.parentNode.insertBefore(elem, refElem.nextSibling);
}
Если нет nextSibling
, то второй аргумент insertBefore
становится null и тогда insertBefore(elem,null)
работает как appendChild
.
В решении нет проверки на существование refElem.parentNode
, поскольку вставка после элемента без родителя — уже ошибка, пусть она возникнет в функции, это нормально.
Напишите функцию removeChildren, которая удаляет всех потомков элемента.
<table>
<tr>
<td>Это</td><td>Все</td><td>Элементы DOM</td>
</tr>
</table>
<ol>
<li>Вася</li>
<li>Петя</li>
<li>Маша</li>
<li>Даша</li>
</ol>
<script>
function removeChildren(elem) { /* ваш код */ }
removeChildren(document.body.children[0]); // очищает таблицу
removeChildren(document.body.children[1]); // очищает список
</script>
P.S. Проверьте ваше решение в IE8.
1) Неправильное решение:
Для начала рассмотрим забавный пример того, как делать не надо:
function removeChildren(elem) {
for(var k=0; k<elem.childNodes.length;k++) {
elem.removeChild(elem.childNodes[k]);
}
}
Если вы попробуете это на практике, то увидите, то это не сработает.
Не сработает потому, что childNodes
всегда начинается 0 и автоматически смещается, когда первый потомок удален(т.е. тот, что был вторым, станет первым), поэтому такой цикл по k
пропустит половину узлов.
2) Решение через DOM:
Правильное решение:
function removeChildren(elem) {
while(elem.lastChild) {
elem.removeChild(elem.lastChild);
}
}
3) Неправильное решение (innerHTML):
Прямая попытка использовать innerHTML
была бы неправильной:
function removeChildren(elem) {
elem.innerHTML = '';
}
Дело в том, что в IE<9 свойство innerHTML
на большинстве табличных элементов (кроме ячеек TH/TD) не работает. Будет ошибка.
4) Верное решение (innerHTML):
Можно завернуть innerHTML
в try/catch
:
function removeChildren(elem) {
try {
elem.innerHTML = '';
} catch(e) {
while(elem.firstChild) {
elem.removeChild(elem.firstChild);
}
}
}
Напишите интерфейс для создания списка.
Для каждого пункта:
prompt
.
Создавайте пункт и добавляйте его к UL.Пример тут: http://ru.lookatcode.com/files/tutorial/browser/dom/createList.html
Если посетитель вводит теги — в списке они показываются как обычный текст.
P.S. prompt
возвращает null, если пользователь нажал ESC.
<!DOCTYPE HTML>
<html>
<body>
<h1>Создание списка</h1>
<script>
var ul = document.createElement('ul');
document.body.appendChild(ul);
while (true) {
var data = prompt("Введите текст для пункта списка", "");
if (data === null) {
break;
}
var li = document.createElement('li');
li.appendChild(document.createTextNode(data));
ul.appendChild(li);
}
</script>
</body>
</html>
Делайте проверку на null в цикле. prompt
возвращает это значение только если был нажат ESC.
Контент в LI добавляйте с помощью document.createTextNode
, чтобы правильно работали <
, >
и т.д.
Напишите функцию, которая создаёт вложенный список UL/LI (дерево) из объекта.
Например:
var data = {
"Рыбы":{
"Форель":{},
"Щука":{}
},
"Деревья":{
"Хвойные":{
"Лиственница":{},
"Ель":{}
},
"Цветковые":{
"Берёза":{},
"Тополь":{}
}
}
};
Синтаксис:
var container = document.getElementById('container');
createTree(container, data); // создаёт
Результат (дерево):
Выберите один из двух способов решения этой задачи:
container.innerHTML
.Если получится – сделайте оба.
Исходный код: http://learn.javascript.ru/play/tutorial/browser/dom/build-tree-src.html
Решения через рекурсию.
1) http://learn.javascript.ru/play/tutorial/browser/dom/build-tree.html.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="container"></div>
<script>
var data = {
"Рыбы":{
"Форель":{},
"Щука":{}
},
"Деревья":{
"Хвойные":{
"Лиственница":{},
"Ель":{}
},
"Цветковые":{
"Берёза":{},
"Тополь":{}
}
}
};
function createTree(container, obj) {
container.innerHTML = createTreeText(obj);
}
function createTreeText(obj) { // отдельная рекурсивная функция
var li = '';
for (var key in obj) {
li += '<li>' + key + createTreeText(obj[key]) + '</li>';
}
if (li) {
var ul = '<ul>' + li + '</ul>'
}
return ul || '';
}
var container = document.getElementById('container');
createTree(container, data);
</script>
</body>
</html>
2) http://learn.javascript.ru/play/tutorial/browser/dom/build-tree-dom.html.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="container"></div>
<script>
var data = {
"Рыбы":{
"Форель":{},
"Щука":{}
},
"Деревья":{
"Хвойные":{
"Лиственница":{},
"Ель":{}
},
"Цветковые":{
"Берёза":{},
"Тополь":{}
}
}
};
function createTree(container, obj) {
container.appendChild( createTreeDom(obj) );
}
function createTreeDom(obj) {
// если нет детей, то рекурсивный вызов ничего не возвращает
// так что вложенный UL не будет создан
if (isObjectEmpty(obj)) return;
var ul = document.createElement('ul');
for (var key in obj) {
var li = document.createElement('li');
li.innerHTML = key;
var childrenUl = createTreeDom(obj[key]);
if (childrenUl) li.appendChild(childrenUl);
ul.appendChild(li);
}
return ul;
}
function isObjectEmpty(obj) {
for (var key in obj) {
return false;
}
return true;
}
var container = document.getElementById('container');
createTree(container, data);
</script>
</body>
</html>
Напишите функцию, которая умеет генерировать календарь для заданной пары (месяц, год).
Календарь должен быть таблицей, где каждый день — это TD. У таблицы должен быть заголовок с названиями дней недели, каждый день — TH.
Синтаксис: createCalendar(id, year, month)
.
Такой вызов должен генерировать текст для календаря месяца month
в году year
, а затем помещать его внутрь элемента с указанным id
.
Например: createCalendar("cal", 2012, 9)
сгенерирует в <div id='cal'></div>
следующий календарь:
ПН | ВТ | СР | ЧТ | ПТ | СБ | ВС |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Начальный документ со стилями http://learn.javascript.ru/play/tutorial/date/calendar_src.html
Или:
<!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>
<meta charset="utf-8">
</head>
<body>
<div id="calendar"></div>
<script>
function createCalendar(id, year, month) {
var elem = document.getElementById(id)
// ... ваш код, который генерирует в elem календарь
}
createCalendar('calendar', 2011, 1)
</script>
</body>
</html>
P.S. Достаточно сгенерировать календарь, кликабельным его делать не нужно.
Для решения задачи сгенерируем таблицу в виде строки: "<table>...</table>"
, а затем присвоим в innerHTML
.
Алгоритм:
d = new Date(year, month-1)
. Это первый день месяца month
(с учетом того, что месяцы в JS начинаются от 0, а не от 1).d.getDay()
, с которого начинается месяц. Создадим их.d
на единицу: d.setDate(d.getDate()+1)
, и добавляем в календарь очередную ячейку, пока не достигли следующего месяца. При этом последний день недели означает вставку перевода строки "</tr><tr>"
.Код решения:
<!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>
<meta charset="utf-8">
</head>
<body>
<div id="calendar"></div>
<script>
function createCalendar(id, year, month) {
var elem = document.getElementById(id);
var mon = month - 1; // месяцы в JS идут от 0 до 11, а не от 1 до 12
var d = new Date(year, mon);
var table = '<table><tr><th>пн</th><th>вт</th><th>ср</th><th>чт</th><th>пт</th><th>сб</th><th>вс</th></tr><tr>';
// заполнить первый ряд от понедельника
// и до дня, с которого начинается месяц
// * * * | 1 2 3 4
for (var i = 0; i < getDay(d); i++) {
table += '<td></td>';
}
// ячейки календаря с датами
while(d.getMonth() == mon) {
table += '<td>'+d.getDate()+'</td>';
if (getDay(d) % 7 == 6) { // вс, последний день - перевод строки
table += '</tr><tr>';
}
d.setDate(d.getDate()+1);
}
// добить таблицу пустыми ячейками, если нужно
if (getDay(d) != 0) {
for (var i = getDay(d); i < 7; i++) {
table += '<td></td>';
}
}
// закрыть таблицу
table += '</tr></table>';
// только одно присваивание innerHTML
elem.innerHTML = table;
}
function getDay(date) { // получить номер дня недели, от 0(пн) до 6(вс)
var day = date.getDay();
if (day == 0) day = 7;
return day - 1;
}
createCalendar("calendar", 2012, 9)
</script>
</body>
</html>