Рубрика «JavaScript»

Общее представление о DOM. Часть 2

Перемещение по иерархическому дереву DOM

Оказавшись внутри древовидной структуры DOM, вы можете перемещаться по ней вверх и вниз, влево и вправо. Ниже приведен перечень наиболее важных свойств каждого узла DOM:

  • firstChild — первый порожденный узел.
  • lastChild — последний порожденный узел.
  • childNodes — все порожденные узлы (в виде массива).
  • parentNode — родительский узел.
  • nextSibling — следующий узел того же уровня (расположенный справа).
  • previousSibling — предыдущий узел того же уровня (расположенный слева).

Кроме того, свойство nodeName возвращает имя дескриптора узла (или #text для текстовых узлов), тогда как свойство nodeValue возвращает значение узла (что особенно полезно для текстовых узлов).

Получение информации об узле

Как упоминалось выше, информацию об узле можно собирать, что очень удобно для работы с произвольными данными DOM.

Если свойство nodeName предоставляет сведения об имени узла (имя дескриптора или #text для текстовых узлов), то свойство nodeValue полезно только для текстовых узлов, поскольку возвращает конкретный текст в искомом узле. Третью категорию информации об узле предоставляет свойство nodeType. По существу, эта информация касается вида узла. Ниже представлены возможные значения свойства nodeType.

  • 1 — дескриптор
  • 2 — атрибут
  • 3 — текст (включая пробелы)
  • 8 — HTML-комментарий
  • 9 — документ
  • 10 — DTD (определение типа документа)
  • 11 — фрагмент

В приведенном ниже фрагменте кода анализируется простая структура DOM и выводится информация, касающаяся всех порожденных узлов.

<script type="text/javascript">
window.onload = function() {
  var s = "";
  with (document.getElementById("para")) {
    for (var i=0; i<childNodes.length; i++) {
      with (childNodes[i]) {
        s += nodeName + ": " + nodeValue + " (" + nodeType + ")\n";
      }
    }
  }
  window.alert(s);
}
</script>
<p id="para"><em>JavaScript</em> Phrasebook</p>

Удаление элементов

Для удаления узла из иерархического дерева DOM служит метод removeChild(), доступный для каждого узла. Однако следует иметь в виду, что этот метод необходимо вызывать из родительского элемента удаляемого узла, указывая последний в качестве параметра. В приведенном ниже примере отображается список узлов и удаляется последний узел после каждого щелчка на соответствующей кнопке.

<script type="text/javascript">
function removeItem() {
  var list = document.getElementById("list");
  if (list.childNodes.length > 0) {
    list.removeChild(list.lastChild);
  }
}
</script>
<ol id="list"><li>Item</li><li>Item</li><li>Item</li><li>Item</li></ol>
<input type="button" onclick="removeItem();" value="Удалить" />

При наличии непосредственного доступа к удаляемому узлу (с помощью свойства curNode) можно применит следующий подход:

curNode.parentNode.removeChild(curNode);

Если этот код вызывается в функции обработки событий для самого узла, то curNode можно заменить на this.

Общее представление о DOM. Часть 1

Название DOM (Document Object Model — объектная модель документа) говорит само за себя. DOM является способом моделирования HTML-документов. В рамках этой модели обеспечивается возможность доступа, навигации и манипулирования HTML-документами. DOM обеспечивает полный контроль над документом, предоставляя полный доступ и позволяя модифицировать содержимое документа.

Для того, чтобы наглядно представить модель DOM для Web-страницы, лучше всего воспользоваться инспектором DOM (DOM Inspector), входящим в состав браузеров Mozilla. В объектной модели документа все элементы страницы располагаются в виде древовидной иерархии.

Каждый дескриптор HTML является узлом такого дерева и связан с соответствующими подузлами и родительскими узлами. Кроме того, у каждого текстового блока имеется свой узел DOM, а точнее, текстовой узел.

Разумеется, в интерфейсе DOM API имеются методы не только для доступа, но и для добавления и удаления отдельных элементов иерархического дерева DOM. Это дает возможность изменить на странице практически все, что угодно.

Доступ к конкретным элементам

Идеальный способ доступа к отдельному элементу на странице во время работы с DOM — присвоить ему уникальный идентификационный номер (ID). В таком случае метод document.getElementById() предоставляет доступ к заданному элементу, после чего можно видоизменить этот элемент, присоединить к нему дочерние элементы или переместиться дальше по дереву DOM.

В приведенном ниже фрагменте кода осуществляется доступ к элементу <p>. Строковое представление класса этого элемента может меняться в зависимости от типа браузера. Так, если MS IE выводит лишь информацию [object], то браузеры Mozilla более многословны, предоставляя больше сведений об искомом элементе: [object HTMLParagraphElement].

<script type="text/javascript">
window.onload = function() {
  window.alert(document.getElementById("para"));
}
</script>
<p id="para">JavaScript Phrasebook</p>

Доступ по имени дескриптора

Другой способ доступа к отдельным элементам на текущей странице — по именам дескрипторов.

Когда приходится работать с рядом элементов, обозначенных одними и теми же дескрипторами HTML (например, со всеми элементами списка или же со всеми абзацами), рекомендуется использовать метод document.getElementsByTagName(). Достаточно указать имя дескриптора, чтобы получить массив всех элементов для их последующей обработки.

В приведенном ниже фрагменте кода осуществляется доступ ко всем элементам <p> и последующий их подсчет.

<script type="text/javascript">
window.onload = function() {
  window.alert(
    document.getElementsByTagName("p") +
    " (" + document.getElementsByTagName("p").length + " elements)");
}
</script>
<p>JavaScript Phrasebook</p>
<p>Sams Publishing</p>

Результат выполнения приведенного выше кода следующий: [object HTMLCollection] (2 elements), т.е. два искомых элемента. Опять же браузер MS IE предоставляет меньше информации, выводя лишь следующую строку: [object] (2 elements).

Календарь на JavaScript

Интерфейс календаря состоит из двух частей:

  1. Шапка, в которой указывается название приложения (Календарь) и осуществляется навигация между годами и месяцами. Изменение текущего года реализовано в виде двух ссылок: ссылки на предыдущий год и ссылки на последующий. В центре указывается текущий год. Аналогичным образом реализовано изменение месяца. Изменение месяца имет одну особенность. Если текущий месяц — январь, то при переходе на предыдущий месяц производится переход на декабрь предыдущего года. Аналогичным образом, если текущий месяц — декабрь, то при переходе на следующий месяц производится переход на январь следующего года. Все ссылки несут одновременно информацию о месяце и годе.
  2. Таблица календаря на один месяц. Таблица календаря имеет заголовок с названиями дней недели. Первым днем в таблице идет понедельник.

В скрипте календаря инициализируются три основных массива данных:

  • months — названия двенадцати месяцев года;
  • monthcountdays — количества дней в месяце;
  • weekdays — сокращенные названия дней недели.

Инициализация массива monthcountdays производится отдельно от массивов moths и weekdays. Предварительно необходимо определить год, для одного из месяцев которого осуществляется построение календаря, так как это необходимо для определения количества дней в феврале. Принцип определения месяца и года основан на поиске в URL текущего HTML-документа названия месяца и номера года. Если в URL название месяца или номер года отсутствуют, то календарь строится на текущие месяц и год.

С точки зрения реализации весь скрипт можно разделить на несколько логических блоков.

  1. Определение текущей даты и сохранение ее значения в переменной currentdate.
  2. Инициализация массива months, содержащего названия двенадцати месяцев года, и массива weekdays, содержащего сокращенные названия дней недели.
  3. Выделение из URL HTML-документа требуемого месяца, для которого будет строиться календарь.
  4. Выделение из URL HTML-документа требуемого года, для которого будет строиться календарь.
  5. Создание объекта даты для первого числа того месяца и года, для которого строится календарь, а затем определение для этой даты дня недели, название которого сохраняется в переменной firstday.
  6. Формирование системы навигации (изменение года и месяца).
  7. Формирование таблица календаря:
    1. формирование заголовка таблицы;
    2. формирование шапки календаря с сокращенными названиями дней недели
    3. формирование первой “пустой” ячейки, если месяц начинается не с понедельника;
    4. формирование ячеек таблицы, соответствующих дням месяца;
    5. формирование последней “пустой” ячейки таблицы, если месяц не заканчивается в воскресенье.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Календарь</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
</head>
<body>
    <script type="text/javascript">
    <!--
        var i;
        var currentdate = new Date();

        var months = new Array( "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь",
                       "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь" );
        var weekday = new Array( "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс" );

        var currentmonth = currentdate.getMonth();
        for(i=0;i<12;i++) {
            if( document.location.href.indexOf( encodeURIComponent(months[i]) ) != -1 ) {
                currentmonth = i;
            }
        }

        var currentyear  = currentdate.getFullYear();
        for(i=1900;i<2100;i++)
            if( document.location.href.indexOf( (new Number(i)).toString() ) != -1 )
                currentyear = i;

        var monthcountday = new Array( 31, (((currentyear%4) == 0 ) ? 29 : 28),
                                       31, 30, 31, 30, 31, 31, 30, 31, 30 ,31 );
         
        var firstday = ((new Date(currentyear,currentmonth,1)).getDay()+6)%7;

        document.write( "<h1 style='text-align:center'>Календарь</h1>"
                        + "<div align='center'>"
                       
                        + "<div><a href='calendar.html?"+encodeURIComponent(months[currentmonth])+","+(currentyear-1)+"'>"+(currentyear-1)+"</A>&nbsp;&nbsp;&nbsp;&nbsp;"
                        + "<strong>" + currentyear + "</strong>"
                        + "&nbsp;&nbsp;&nbsp;&nbsp;<a href='calendar.html?"+encodeURIComponent(months[currentmonth])+","+(currentyear+1)+"'>"+(currentyear+1)+"</a></div>"

                        + ((currentmonth!=0) ? "<div><a href='calendar.html?"+encodeURIComponent(months[currentmonth-1])+","+currentyear+"'>"+months[currentmonth-1]+"</a>&nbsp;&nbsp;&nbsp;&nbsp;"
                                             : "<div><a href='calendar.html?"+encodeURIComponent(months[11])+","+(currentyear-1)+"'>"+months[11]+" of "+(currentyear-1)+"</A>&nbsp;&nbsp;&nbsp;&nbsp;")
                        + "<strong>" + months[currentmonth] + "</strong>"
                        + ((currentmonth!=11) ? "&nbsp;&nbsp;&nbsp;&nbsp;<a href='calendar.html?"+encodeURIComponent(months[currentmonth+1])+","+currentyear+"'>"+months[currentmonth+1]+"</a></div>"
                                              : "&nbsp;&nbsp;&nbsp;&nbsp;<a href='calendar.html?"+encodeURIComponent(months[0])+","+(currentyear+1)+"'>"+months[0]+" of "+(currentyear+1)+"</a></div>")

                        + "</div>" );

        document.write ( "<table width='300' align='center' border='1' bgcolor='#EEEEEE'><tr>" );

        for(i=0;i<7;i++)
            document.write( "<th bgcolor='blue'><span style='color:white'>" + weekday[ i ] + "</span></th>" );

        if( firstday != 0 )
            document.write( "</tr><tr align='center'><td colspan='" + firstday + "'>&nbsp;</td>" );

        for(i=0;i<monthcountday[currentmonth];i++)
        {
            if((i+firstday)%7==0)
                document.write ( "</tr><tr align='center'>" );
            document.write( "<td>" + (i+1) + "</td>" );
        }

        if( (monthcountday[currentmonth]+firstday)%7 != 0 )
            document.write( "<td colspan='" + (7-((monthcountday[currentmonth]+firstday)%7)) + "'>&nbsp;</td>" );

        document.write( "</tr></table>" );
    //-->
    </script>

</body>
</html>