Чем является каждый html тег в dom
Перейти к содержимому

Чем является каждый html тег в dom

  • автор:

DOM tree

According to the Document Object Model (DOM), every HTML tag is an object. Nested tags are “children” of the enclosing one. The text inside a tag is an object as well.

All these objects are accessible using JavaScript, and we can use them to modify the page.

For example, document.body is the object representing the <body> tag.

Running this code will make the <body> red for 3 seconds:

Here we used style.background to change the background color of document.body , but there are many other properties, such as:

  • innerHTML – HTML contents of the node.
  • offsetWidth – the node width (in pixels)
  • …and so on.

Soon we’ll learn more ways to manipulate the DOM, but first we need to know about its structure.

An example of the DOM

Let’s start with the following simple document:

The DOM represents HTML as a tree structure of tags. Here’s how it looks:

On the picture above, you can click on element nodes and their children will open/collapse.

Every tree node is an object.

Tags are element nodes (or just elements) and form the tree structure: <html> is at the root, then <head> and <body> are its children, etc.

The text inside elements forms text nodes, labelled as #text . A text node contains only a string. It may not have children and is always a leaf of the tree.

For instance, the <title> tag has the text "About elk" .

Please note the special characters in text nodes:

  • a newline: ↵ (in JavaScript known as \n )
  • a space: ␣

Spaces and newlines are totally valid characters, like letters and digits. They form text nodes and become a part of the DOM. So, for instance, in the example above the <head> tag contains some spaces before <title> , and that text becomes a #text node (it contains a newline and some spaces only).

There are only two top-level exclusions:

  1. Spaces and newlines before <head> are ignored for historical reasons.
  2. If we put something after </body> , then that is automatically moved inside the body , at the end, as the HTML spec requires that all content must be inside <body> . So there can’t be any spaces after </body> .

In other cases everything’s straightforward – if there are spaces (just like any character) in the document, then they become text nodes in the DOM, and if we remove them, then there won’t be any.

Here are no space-only text nodes:

Browser tools (to be covered soon) that work with DOM usually do not show spaces at the start/end of the text and empty text nodes (line-breaks) between tags.

Developer tools save screen space this way.

On further DOM pictures we’ll sometimes omit them when they are irrelevant. Such spaces usually do not affect how the document is displayed.

Autocorrection

If the browser encounters malformed HTML, it automatically corrects it when making the DOM.

For instance, the top tag is always <html> . Even if it doesn’t exist in the document, it will exist in the DOM, because the browser will create it. The same goes for <body> .

As an example, if the HTML file is the single word "Hello" , the browser will wrap it into <html> and <body> , and add the required <head> , and the DOM will be:

While generating the DOM, browsers automatically process errors in the document, close tags and so on.

A document with unclosed tags:

…will become a normal DOM as the browser reads tags and restores the missing parts:

An interesting “special case” is tables. By DOM specification they must have <tbody> tag, but HTML text may omit it. Then the browser creates <tbody> in the DOM automatically.

DOM-structure will be:

You see? The <tbody> appeared out of nowhere. We should keep this in mind while working with tables to avoid surprises.

Other node types

There are some other node types besides elements and text nodes.

For example, comments:

We can see here a new tree node type – comment node, labeled as #comment , between two text nodes.

We may think – why is a comment added to the DOM? It doesn’t affect the visual representation in any way. But there’s a rule – if something’s in HTML, then it also must be in the DOM tree.

Everything in HTML, even comments, becomes a part of the DOM.

Even the <!DOCTYPE. > directive at the very beginning of HTML is also a DOM node. It’s in the DOM tree right before <html> . Few people know about that. We are not going to touch that node, we even don’t draw it on diagrams, but it’s there.

The document object that represents the whole document is, formally, a DOM node as well.

There are 12 node types. In practice we usually work with 4 of them:

  1. document – the “entry point” into DOM.
  2. element nodes – HTML-tags, the tree building blocks.
  3. text nodes – contain text.
  4. comments – sometimes we can put information there, it won’t be shown, but JS can read it from the DOM.

See it for yourself

To see the DOM structure in real-time, try Live DOM Viewer. Just type in the document, and it will show up as a DOM at an instant.

Another way to explore the DOM is to use the browser developer tools. Actually, that’s what we use when developing.

To do so, open the web page elk.html, turn on the browser developer tools and switch to the Elements tab.

It should look like this:

You can see the DOM, click on elements, see their details and so on.

Please note that the DOM structure in developer tools is simplified. Text nodes are shown just as text. And there are no “blank” (space only) text nodes at all. That’s fine, because most of the time we are interested in element nodes.

Clicking the button in the left-upper corner allows us to choose a node from the webpage using a mouse (or other pointer devices) and “inspect” it (scroll to it in the Elements tab). This works great when we have a huge HTML page (and corresponding huge DOM) and would like to see the place of a particular element in it.

Another way to do it would be just right-clicking on a webpage and selecting “Inspect” in the context menu.

At the right part of the tools there are the following subtabs:

  • Styles – we can see CSS applied to the current element rule by rule, including built-in rules (gray). Almost everything can be edited in-place, including the dimensions/margins/paddings of the box below.
  • Computed – to see CSS applied to the element by property: for each property we can see a rule that gives it (including CSS inheritance and such).
  • Event Listeners – to see event listeners attached to DOM elements (we’ll cover them in the next part of the tutorial).
  • …and so on.

The best way to study them is to click around. Most values are editable in-place.

Interaction with console

As we work the DOM, we also may want to apply JavaScript to it. Like: get a node and run some code to modify it, to see the result. Here are few tips to travel between the Elements tab and the console.

  1. Select the first <li> in the Elements tab.
  2. Press Esc – it will open console right below the Elements tab.

Now the last selected element is available as $0 , the previously selected is $1 etc.

We can run commands on them. For instance, $0.style.background = ‘red’ makes the selected list item red, like this:

That’s how to get a node from Elements in Console.

There’s also a road back. If there’s a variable referencing a DOM node, then we can use the command inspect(node) in Console to see it in the Elements pane.

Or we can just output the DOM node in the console and explore “in-place”, like document.body below:

That’s for debugging purposes of course. From the next chapter on we’ll access and modify DOM using JavaScript.

The browser developer tools are a great help in development: we can explore the DOM, try things and see what goes wrong.

Summary

An HTML/XML document is represented inside the browser as the DOM tree.

  • Tags become element nodes and form the structure.
  • Text becomes text nodes.
  • …etc, everything in HTML has its place in DOM, even comments.

We can use developer tools to inspect DOM and modify it manually.

Here we covered the basics, the most used and important actions to start with. There’s an extensive documentation about Chrome Developer Tools at https://developers.google.com/web/tools/chrome-devtools. The best way to learn the tools is to click here and there, read menus: most options are obvious. Later, when you know them in general, read the docs and pick up the rest.

DOM nodes have properties and methods that allow us to travel between them, modify them, move around the page, and more. We’ll get down to them in the next chapters.

Chapter 12 Document Object Model (DOM)

The primary purpose of using JavaScript in a web page is to make that page interactive: the JavaScript language is used to program logical decisions that will effect what is shown on the page. It does this primarily by changing the HTML rendered by the browser. For example, JavaScript can be used to change the text inside a <p> , add addition <li> elements to a list, or to give a <div> a new CSS class attribute. The programmatic representation of the HTML elements currently being shown by the browser is known as the Document Object Model (DOM). In web programming JavaScript code is used to modify the DOM (HTML elements currently being shown by the browser) in response to user input, thereby making the page interactive. This chapter introduces the Document Object Model and how to use JavaScript to manipulate it through user-driven interaction.

12.1 The DOM API

As you should recall from Chapter 3, HTML elements can be nested, allowing us to consider a webpage as a “tree” of elements:

An example DOM tree (a tree of HTML elements).

  • A tree is a hierarchical data structure, where each element (called a node) contains references to child elements. Following the arboreal metaphor, the “start” of the tree is called the root note, hierarchical sequences of nodes are called branches, and a node that does not have any children is called a leaf.

Considering a web page’s content to be a tree of HTML elements is one way to model (represent) the structure of that information. This particular model of a web document (as a tree of object nodes) is called the Document Object Model, or DOM for short. In many ways the DOM is the HTML (though the HTML rendered in the browser, not the .html source code you’ve written)! Thus we can refer to the web page’s content as “the DOM”, and an HTML element as a “DOM element”.

  • Note that even “plain text” content (e.g., what is inside a <p> tag) are considered nodes in the DOM tree—they are “text content” nodes (instead of “element nodes”).

Moreover, the DOM also provides an Application Programming Interface (API) which allows computer applications to programmatically (e.g., through JavaScript code) interact with it: accessing and manipulating the tree of elements. As you may recall from previous courses, an API is often a set of functions and variables that can be used give instructions to a program. The DOM API is no different: it is a group of functions you can call and variables (usually Object properties) you can adjust to change the rendered web content. You write code to call these functions in order to make a page interactive.

Global Variables

You can programmatically access the API in JavaScript by utilizing a set of global variables. Global variables are variables that are “globally” scoped: they are available anywhere in the program (not just within a particular function).

An important programming style rule is to minimize the use of global variables. Try to avoid creating too many new globals yourself!

Global variables in JavaScript are almost always Objects that have methods as their values. For example, the JavaScript language itself provides a global Math object that has includes a number of function properties (e.g., sqrt() , floor() , etc.).

  • In fact, the console object is another global variable provided by the JavaScript runtime (whether inside the browser or inside Node.js)!

The web browser also provides a number of global variables that you can use. For example window is a global object that represents the browser itself. You can use this object to get information about the browser:

While these examples are included for completeness, most window functions are rarely used and should be avoided. Popups with the window.alert() function are inelegant, interrupt the user’s actions, and produce a bad user experience—you should instead use in-window alerting options instead (such as showing a <p > ). Browser control functions such as scrollTo() are non-standard and can vary drastically across systems and platforms. Proceed with caution when using window functions!

12.2 DOM Manipulation

While window represents the Browser, the DOM itself is represented by the document global object— document is the DOM (the current HTML rendered in the browser). You access properties and call methods of this object in order to manipulate the content displayed in the browser!

Referencing HTML Elements

In order to manipulate the DOM elements in a page, you first need to get a reference to the element you want to change—that is, you need a variable that refers to that element. You can get these variable references by using one of the document “selector” functions:

The document.querySelector() is by far the most flexible and easy to use of these methods: it can easily do the same as all the other methods (just put in an id, class, or element selector). You should always use querySelector() .

Note that the methods that return multiple nodes (e.g., querySelectorAll ) return a NodeList object. While this is like an array (you can access elements via index through bracket notation and it has a .length property), it is not an array: meaning it doesn’t support methods like forEach() and map() across all browsers. If you need to iterate through a NodeList , you should use a regular for loop. But in practice, you’re much more likely to only work with single elements at a time.

Modifying HTML Elements

Once you have a reference to an element, you access properties and call methods on that object in order to modify its state in the DOM—which will in turn modify how it currently is displayed on the page. Thus by modifying these objects, you are dynamically changing the web page’s content!

Important: setting these properties do not change the .html source code file! Instead, they just change the rendered DOM elements (think: the content stored in the computer’s memory rather than in a file). If you refresh the page, the content will go back to how the .html source code file specifies it should appear—unless that also loads the script that modifies the DOM. What is shown on the page is the HTML with the JavaScript modifications added in.

Changing Content

You can use JavaScript to access and modify the content of a DOM element (e.g., the stuff between the start and close tags):

The textContent property of the element refers to all of the content, but considered as “plain text” this means that it is considered a “safe” property: you can assign strings that contain’s HTML code (e.g., <em>Hello</em> ), but that code will be escaped and not interpreted as HTML (instead the < and > will be written out as if you had used HTML entities). The .innerHTML property, on the other hand, is “not safe”: any HTML included in the String you assign to it will be converted into DOM elements. This makes it not a great property to use unless unless you are absolutely certain the content came from a trusted source.

The innerHTML property should be used primarily for including inline elements such as <em> or <strong> . For more complex HTML content, it is cleaner code (separation of concerns!) to explicitly create new elements—see below for details.

You can “clear” the content of an element by setting it’s content to be an empty string ( » ):

Changing Attributes

You can also change the attributes of individual elements. Each attribute defined in the HTML specification is typically exposed as a property of the element object:

You cannot access element.class or element.style attributes directly in this way; see below for specifics on changing the CSS of an element.

You can alternatively modify element attributes by using the methods getAttribute() (passing it which attribute to access) and setAttribute() (passing it which attribute to modify and what value to assign to that attribute):

These methods will let you interact with attributes that are not defined by the HTML spec, such as data- attribute. However, they don’t work with certain element attributes (such as the value attribute of an <input> element). Other elements may have their own special DOM properties: see the DOM Documentation for a list of HTML interfaces.

Changing Element CSS

It is possible to modify the CSS classes (and even inline styling) of an element. But rather than using the class property like with other attributes, you instead access the classList property. On modern browsers (IE 10 or later), this property supports methods .add() and .remove() for adding and removing classes from the list:

While IE 10+ does support these methods, it doesn’t support multiple arguments for them (so you can’t add multiple classes in a single method call). If you need to support older browsers (including any version of IE), you can instead modify the .className property as if it were a String:

The classList methods work perfectly on Microsoft Edge.

It is also possible to access and modify individual CSS properties of elements through the DOM element’s style property. .style references an Object whose keys are the CSS property names (but written in camelCase instead of kabob-case)

In general, you should modify element CSS by changing the class of the element, rather than specific style properties.

Modifying the DOM Tree

In addition to modifying the individual DOM elements, it is also possible to access and modify the DOM tree itself! That is, you can create new elements and add them to the tree (read: webpage), remove elements from the tree, or pluck them out of the tree and insert them somewhere else!

First, note that each JavaScript DOM element has read-only properties referring to its parent, children, and sibling elements:

Note that these properties only deal with HTML elements—text content nodes are ignored. You can instead use equivalent properties parentNode and childNodes to also consider text content nodes.

SVG content doesn’t support parentElement , but does support parentNode .

You can also call methods to create and add new HTML DOM elements to the tree. The document.createElement() function is used to create a new HTML element. However this element is not created as a part of the tree (after all, you haven’t specified where it would put into the page)! Thus you need to also use a method such as appendChild to add that new element as a child of another element:

The appendChild() method is considered a cleaner approach than just modifying the innerHTML property, as it allows you to adjust the DOM tree without erasing what was previously there. A common practice is to use document.createElement() to create a block element, then set the innerHTML of that element to its content (which can include inline elements), and then use appendChild to add the new block element to the tree at the desired location.

Accessibility

Whenever you learn a new technology, you should ask: how does this affect accessibility? With the JavaScript code modifying the rendered DOM, it is possible that the content of a page will change after it has been read by a screen reader. And while a sighted user will likely be able to see the change visually, a screen reader has no way of knowing that something on the page is different unless you tell it.

You can let screen readers know that an element in a page may have its content change in the future by making that element into an ARIA Live Region. Live regions are “watched” by assistive technologies, and whenever the content changes they will speak the new content to the reader as if it were being read for the first time.

You make an element into a live region by giving it the aria-live attribute:

The value assigned to the aria-live attribute is the “politeness level”, which specifies the priority by which the screen reader should read the change. The most common option (that you should almost always use) is «polite» , which indicates that the changed text will be read only once the user has paused whatever is currently being read. A «polite» alert doesn’t interrupt the currently being read text or description, but instead will be injected when there is a break (if the current reading goes on for too long, then the new content will not be spoken).

  • The other option is «assertive» , which indicates that the new content should be spoken as soon as it changes, possibly interrupting other content. This should only be used for important information (like alerts, warnings, or errors), as it can interrupt the user’s flow in ways that are very disorienting. In short: always be polite!

12.3 Listening for Events

In order to make a page interactive (that is, able to change in response to user actions), you need to be able to respond to user events. Whenever a user interacts with a computer, the operating system announces that interaction as an event—the event of a button being clicked, the event of the mouse being moved, the event of a keyboard key being pressed, etc. These events are broadcast to the entire system, allowing any application (including the browser) to “respond” the occurrence of the event, such as by executing a particular JavaScript function.

Thus in order to respond to user actions (and the events those actions generate), we need to define a function that will be executed when the event occurs. You will define a function as normal, but the function will not get called by you as a particular step in your script. Instead, the function you specify will be executed by the system when an event occurs, which will be at some indeterminate time in the future. This process is known as event-driven programming. It is also an example of asynchronous programming: in which statements are not executed in a single order one after another (“synchronously”), but may occur “out of order” or even at the same time! (For more about working with asynchronous programming, see Chapter 14).

In order for your script to respond to user events, you need to register an event listener. This is a bit like following someone on social media: you specify that you want to “listen” for updates from that person, as well as what you want to do when you “hear” some news from that person.

  • Specifying that you want Slack to notify you when your name is mentioned is another good analogy!

The DOM API allows you to register an event listener by call the .addEventListener() on a selected element (e.g., on the element that you want to listen to). This method takes two arguments: a string representing what kind of event you want to listen for, and a callback function to execute when you hear that event:

When the button is clicked by the user, it will “shout” out a ‘click’ event (“I was clicked! I was clicked!”). Because you have set up a listener (an alert/notification) for such an occurrence, your script will be able to do something—and that something that it will do is run the specified callback function.

It’s like you handed someone a recipe and told them “when I call you, bake this cake!”

It is much more common to use an anonymous function as the callback:

Note that this listener only applies to that particular button—if you wanted to respond to a different button, you’d need to register a separate listener! Also, as the method name implies, it is possible to add multiple listeners (callbacks) to the same element for the same event: all of them will be executed “at once”.

The event callback will be passed in a single argument: an object representing the “event” that occurred. (Since all parameters are optional in JavaScript, and it wasn’t used in the above example, it wasn’t included in the callback definition). This event includes information such as where the event occurred (in x,y coordinates), what DOM element caused the event, and more:

Also note that sometimes you want to stop the “normal” results of an event from occurring. For example, perhaps you don’t want a button to do it’s normal button thing (such as submitting a form), and instead want to provide your own custom behavior. To support this, you can “interrupt the event” by calling the following methods on the event:

Types of Events

There are numerous different events that you can listen for, including:

Mouse Events such as ‘click’ . The event.offsetX and event.offsetY will provide (x,y) coordinates for the clicks location relative to the target element; you can use clientX/Y for coordinates relative to the browser window, or pageX/Y for coordinates relative to the document (regardless of scrolling). See this post for details, and this page for an example.

Other mouse events include ‘dblclick’ (double-click), ‘mousedown’ (mouse button is pressed down, may be held), ‘hover’ (mouse hover), ‘mouseenter’ (mouse moves over element), ‘mousemove’ (mouse moves over element), and ‘mouseleave’ (mouse moves of of element).

Keyboard Events such as ‘keydown’ . The event.key property is used to determine which key was pressed, giving a predefined key value you can check:

The event object also has properties to check if any “modifier keys” such as shift, control, or meta (Windows/command) are held when the event occurs.

Note that you almost always want to respond to the ‘keydown’ and ‘keyup’ events; the ‘keypressed’ event is sent later and only applies to non-modifier keys.

Window Events are event created by the window global, which we are also able to register event listeners on! For example, the ‘resize’ event can be used to identify when the window has changed size (e.g., if you want to make the content responsive as well as the CSS):

(See the documentation for advise on using this callback)

Additionally, the window global defines a special event callback that occurs when the web page has finished loading. You can assign your own function to this callback to run code only after the webpage has loaded (e.g., for scripts specified in the <head> ):

Style guideline: always register event listeners in the JavaScript—do not utilize the HTML attributes such as onclick . This is to help keep concerns separated: the HTML should not need to know anything about the JavaScript that is utilized (since the browser may not even support JavaScript!), but it’s okay for the JavaScript to rely on and modify the HTML.

Выразительный JavaScript: Document Object Model (объектная модель документа)

Когда вы открываете веб-страницу в браузере, он получает исходный текст HTML и разбирает (парсит) его примерно так, как наш парсер из главы 11 разбирал программу. Браузер строит модель структуры документа и использует её, чтобы нарисовать страницу на экране.

Это представление документа и есть одна из игрушек, доступных в песочнице JavaScript. Вы можете читать её и изменять. Она изменяется в реальном времени – как только вы её подправляете, страница на экране обновляется, отражая изменения.

Структура документа

Можно представить HTML как набор вложенных коробок. Теги вроде <body> и </body> включают в себя другие теги, которые в свою очередь включают теги, или текст. Вот вам пример документа из предыдущей главы:

У этой страницы следующая структура:

Структура данных, использующаяся браузером для представления документа, отражает его форму. Для каждой коробки есть объект, с которым мы можем взаимодействовать и узнавать про него разные данные – какой тег он представляет, какие коробки и текст содержит. Это представление называется Document Object Model (объектная модель документа), или сокращённо DOM.

Мы можем получить доступ к этим объектам через глобальную переменную document. Её свойство documentElement ссылается на объект, представляющий тег . Он также предоставляет свойства head и body, в которых содержатся объекты для соответствующих элементов.

Деревья

Вспомните синтаксические деревья из главы 11. Их структура удивительно похожа на структуру документа браузера. Каждый узел может ссылаться на другие узлы, потомки, которые, в свою очередь, могут иметь своих собственных потомков. Эта структура – типичный пример вложенных структур, где элементы содержат подэлементы, похожие на них самих.

Мы зовём структуру данных деревом, когда она разветвляется, не имеет циклов (узел не может содержать сам себя явно или неявно), и имеет единственный ярко выраженный «корень». В случае DOM в качестве корня выступает document.documentElement.

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

У типичного дерева есть разные узлы. У синтаксического дерева языка Egg были переменные, значения и приложения. У приложений всегда были дочерние ветви, а переменные и значения были «листьями», то есть узлами без дочерних ответвлений.

То же и у DOM. Узлы для обычных элементов, представляющих теги HTML, определяют структуру документа. У них могут быть дочерние узлы. Пример такого узла — document.body. Некоторые из этих дочерних узлов могут оказаться листьями – например, текст или комментарии (в HTML комментарии записываются между символами <!— и —> ).

У каждого узлового объекта DOM есть свойство nodeType, содержащее цифровой код, определяющий тип узла. У обычных элементов он равен 1, что также определено в виде свойства-константы document.ELEMENT_NODE. У текстовых узлов, представляющих отрывки текста, он равен 3 (document.TEXT_NODE). У комментариев — 8 (document.COMMENT_NODE).

То есть, вот ещё один способ графически представить дерево документа:

Листья – текстовые узлы, а стрелки показывают взаимоотношения отец-ребёнок между узлами.

Стандарт

Использовать загадочные цифры для представления типа узла – это подход не в стиле JavaScript. Позже мы встретимся с другими частями интерфейса DOM, которые тоже кажутся чуждыми и нескладными. Причина в том, что DOM разрабатывался не только для JavaScript. Он пытается определить интерфейс, не зависящий от языка, который можно использовать и в других системах – не только в HTML, но и в XML, который представляет из себя формат данных общего назначения с синтаксисом, напоминающим HTML.

Получается неудобно. Хотя стандарты – и весьма полезная штука, в нашем случае преимущество независимости от языка не такое уж и полезное. Лучше иметь интерфейс, хорошо приспособленный к языку, который вы используете, чем интерфейс, который будет знаком при использовании разных языков.

Чтобы показать неудобную интеграцию с языком, рассмотрим свойство childNodes, которое есть у узлов DOM. В нём содержится объект, похожий на массив, со свойством length, и пронумерованные свойства для доступа к дочерним узлам. Но это – экземпляр типа NodeList, не настоящий массив, поэтому у него нет методов вроде slice или forEach.

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

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

Обход дерева

Узлы DOM содержат много ссылок на соседние. Это показано на диаграмме:

Хотя тут показано только по одной ссылке каждого типа, у каждого узла есть свойство parentNode, указывающего на его родительский узел. Также у каждого узла-элемента (тип 1) есть свойство childNodes, указывающее на массивоподобный объект, содержащий его дочерние узлы.

В теории можно пройти в любую часть дерева, используя только эти ссылки. Но JavaScript предоставляет нам много дополнительных вспомогательных ссылок. Свойства firstChild и lastChild показывают на первый и последний дочерний элементы, или содержат null у тех узлов, у которых нет дочерних. previousSibling и nextSibling указывают на соседние узлы – узлы того же родителя, что и текущего узла, но находящиеся в списке сразу до или после текущей. У первого узла свойство previousSibling будет null, а у последнего nextSibling будет null.

При работе с такими вложенными структурами пригождаются рекурсивные функции. Следующая ищет в документе текстовые узлы, содержащие заданную строку, и возвращает true, когда находит:

Свойства текстового узла nodeValue содержит строчку текста.

Поиск элементов

Часто бывает полезным ориентироваться по этим ссылкам между родителями, детьми и родственными узлами и проходить по всему документу. Однако если нам нужен конкретный узел в документе, очень неудобно идти по нему, начиная с document.body и тупо перебирая жёстко заданный в коде путь. Поступая так, мы вносим в программу допущения о точной структуре документа – а её мы позже можем захотеть поменять. Другой усложняющий фактор – текстовые узлы создаются даже для пробелов между узлами. В документе из примера у тега body не три дочерних (h1 и два p), а целых семь: эти три плюс пробелы до, после и между ними.

Так что если нам нужен атрибут href из ссылки, мы не должны писать в программе что-то вроде: «второй ребёнок шестого ребёнка document.body». Лучше бы, если б мы могли сказать: «первая ссылка в документе». И так можно сделать:

У всех узлов-элементов есть метод getElementsByTagName, собирающий все элементы с данным тэгом, которые происходят (прямые или не прямые потомки) от этого узла, и возвращает его в виде массивоподобного объекта.

Чтобы найти конкретный узел, можно задать ему атрибут id и использовать метод document.getElementById.

Третий метод – getElementsByClassName, который, как и getElementsByTagName, ищет в содержимом узла-элемента и возвращает все элементы, содержащие в своём классе заданную строчку.

Меняем документ

Почти всё в структуре DOM можно менять. У узлов-элементов есть набор методов, которые используются для их изменения. Метод removeChild удаляет заданный дочерний узел. Для добавления узла можно использовать appendChild, который добавляет узел в конец списка, либо insertBefore, добавляющий узел, переданную первым аргументом, перед узлом, переданным вторым аргументом.

Узел может существовать в документе только в одном месте. Поэтому вставляя параграф «Три» перед параграфом «Один» мы фактически удаляем его из конца списка и вставляем в начало, и получаем «Три/Один/Два». Все операции по вставке узла приведут к его исчезновению с текущей позиции (если у него таковая была).

Метод replaceChild используется для замены одного дочернего узла другим. Он принимает два узла: новый, и тот, который надо заменить. Заменяемый узел должен быть дочерним узлом того элемента, чей метод мы вызываем. Как replaceChild, так и insertBefore в качестве первого аргумента ожидают получить новый узел.

Создание узлов

В следующем примере нам надо сделать скрипт, заменяющий все картинки (тег <img> ) в документе текстом, содержащимся в их атрибуте “alt”, который задаёт альтернативное текстовое представление картинки.

Для этого надо не только удалить картинки, но и добавить новые текстовые узлы им на замену. Для этого мы используем метод document.createTextNode.

Получая строку, createTextNode даёт нам тип 3 узла DOM (текстовый), который мы можем вставить в документ, чтобы он был показан на экране.

Цикл по картинкам начинается в конце списка узлов. Это сделано потому, что список узлов, возвращаемый методом getElementsByTagName (или свойством childNodes) постоянно обновляется при изменениях документа. Если б мы начали с начала, удаление первой картинки привело бы к потере списком первого элемента, и во время второго прохода цикла, когда i равно 1, он бы остановился, потому что длина списка стала бы также равняться 1.

Если вам нужно работать с фиксированным списком узлов вместо «живого», можно преобразовать его в настоящий массив при помощи метода slice.

Для создания узлов-элементов (тип 1) можно использовать document.createElement. Метод принимает имя тега и возвращает новый пустой узел заданного типа. Следующий пример определяет инструмент elt, создающий узел-элемент и использующий остальные аргументы в качестве его детей. Эта функция потом используется для добавления дополнительной информации к цитате.

Атрибуты

К некоторым атрибутам элементов, типа href у ссылок, можно получить доступ через одноимённое свойство объекта. Это возможно для ограниченного числа часто используемых стандартных атрибутов.

Но HTML позволяет назначать узлам любые атрибуты. Это полезно, т.к. позволяет вам хранить дополнительную информацию в документе. Если вы придумаете свои названия атрибутов, их не будет среди свойств узла-элемента. Вместо этого вам надо будет использовать методы getAttribute и setAttribute для работы с ними.

Рекомендую перед именами придуманных атрибутов ставить “data-“, чтобы быть уверенным, что они не конфликтуют с любыми другими. В качестве простого примера мы напишем подсветку синтаксиса, который ищет теги <pre> (“preformatted”, предварительно отформатированный – используется для кода и простого текста) с атрибутом data-language (язык) и довольно грубо пытается подсветить ключевые слова в языке.

Функция highlightCode принимает узел <pre> и регулярку (с включённой настройкой global), совпадающую с ключевым словом языка программирования, которое содержит элемент.

Свойство textContent используется для получения всего текста узла, а затем устанавливается в пустую строку, что приводит к очищению узла. Мы в цикле проходим по всем вхождениям выражения keyword, добавляем между ними текст в виде простых текстовых узлов, а совпавший текст (ключевые слова) добавляем, заключая их в элементы <strong> (жирный шрифт).

Мы можем автоматически подсветить весь код страницы, перебирая в цикле все элементы <pre>, у которых есть атрибут data-language, и вызывая на каждом highlightCodeс правильной регуляркой.

Есть один часто используемый атрибут, class, имя которого является ключевым словом в JavaScript. По историческим причинам, когда старые реализации JavaScript не умели обращаться с именами свойств, совпадавшими с ключевыми словами, этот атрибут доступен через свойство под названием className. Вы также можете получить к нему доступ по его настоящему имени “class” через методы getAttribute и setAttribute.

Расположение элементов (layout)

Вы могли заметить, что разные типы элементов располагаются по-разному. Некоторые, типа параграфов <p> и заголовков <h1> растягиваются на всю ширину документа и появляются на отдельных строках. Такие элементы называют блочными. Другие, как ссылки <a> или акцентированный текст <strong> появляются на одной строчке с окружающим их текстом. Они называются встроенными (inline).

Для любого документа браузеры могут вычислить расположение элементов, раскладку, в которой у каждого элемента будет размер и положение на основе его типа и содержимого. Затем эта раскладка используется для создания внешнего вида документа.

Размер и положение элемента можно узнать через JavaScript. Свойства offsetWidth и offsetHeight выдают размер в пикселях, занимаемый элементом. Пиксель – основная единица измерений в браузерах, и обычно соответствует размеру минимальной точки экрана. Сходным образом, clientWidth и clientHeight дают размер внутренней части элемента, не считая бордюра (или, как говорят некоторые, поребрика).

Самый эффективный способ узнать точное расположение элемента на экране – метод getBoundingClientRect. Он возвращает объект со свойствами top, bottom, left, и right (сверху, снизу, слева и справа), которые содержат положение элемента относительно левого верхнего угла экрана в пикселях. Если вам надо получить эти данные относительно всего документа, вам надо прибавить текущую позицию прокрутки, которая содержится в глобальных переменных pageXOffset и pageYOffset.

Разбор документа – задача сложная. В целях быстродействия браузерные движки не перестраивают документ каждый раз после его изменения, а ждут так долго. как это возможно. Когда программа JavaScript, изменившая документ, заканчивает работу, браузеру надо будет просчитать новую раскладку страницы, чтобы вывести изменённый документ на экран. Когда программа запрашивает позицию или размер чего-либо, читая свойства типа offsetHeight или вызывая getBoundingClientRect, для предоставления корректной информации тоже необходимо рассчитывать раскладку.

Программа, которая периодически считывает раскладку DOM и изменяет DOM, заставляет браузер много раз пересчитывать раскладку, и в связи с этим будет работать медленно. В следующем примере есть две разные программы, которые строят линию из символов X шириной в 2000 пикс, и измеряют время работы.

Стили

Мы видели, что разные элементы HTML ведут себя по-разному. Некоторые показываются в виде блоков, другие встроенные. Некоторые добавляют визуальный стиль – например, <strong> делает жирным текст и <a> делает текст подчёркнутым и синим.

Внешний вид картинки в теге <img> или то, что ссылка в теге <a> при клике открывает новую страницу, связано с типом элемента. Но основные стили, связанные с элементом, вроде цвета текста или подчёркивания, могут быть нами изменены. Вот пример использования свойства style (стиль):

Атрибут «style» может содержать одно или несколько объявлений свойств (color), за которым следует двоеточие и значение. В случае нескольких объявлений они разделяются точкой с запятой: “color: red; border: none”.

Много всякого можно изменить при помощи стилей. Например, свойство display контролирует, показывается ли элемент в блочном или встроенном виде.

Блочный элемент выводится отдельным блоком на собственной строке, а последний вообще не виден – display: none отключает показ элементов. Таким образом можно прятать элементы. Обычно это предпочтительно полному удалению их из документа, потому что их легче потом при необходимости снова показать.

Код JavaScript может напрямую действовать на стиль элемента через свойство узла style. В нём содержится объект, имеющий свойства для всех свойств стилей. Их значения – строки, в которые мы можем писать для смены какого-то аспекта стиля элемента.

Некоторые имена свойств стилей содержат дефисы, например font-family. Так как с ними неудобно было бы работать в JavaScript (пришлось бы писать style[«font-family»]), названия свойств в объекте стилей пишутся без дефиса, а вместо этого в них появляются прописные буквы: style.fontFamily.

Каскадные стили

Система стилей в HTML называется CSS (Cascading Style Sheets, каскадные таблицы стилей). Таблица стилей – набор стилей в документе. Его можно писать внутри тега <style> :

«Каскадные» означает, что несколько правил комбинируются для получения окончательного стиля документа. В примере на стиль по умолчанию для <strong> , который делает текст жирным, накладывается правило из тега <style> , по которому добавляется font-style и цвет.

Когда значение свойства определяется несколькими правилами, приоритет остаётся у более поздних. Если бы стиль текста в <style> включал правило font-weight: normal, конфликтующее со стилем по умолчанию, то текст был бы обычный, а не жирный. Стили, которые применяются к узлу через атрибут style, имеют наивысший приоритет.

В CSS возможно задавать не только название тегов. Правило для .abc применяется ко всем элементам, у которых указан класс “abc”. Правило для #xyz применяется к элементу с атрибутом id равным “xyz” (атрибуты id необходимо делать уникальными для документа).

Приоритет самых поздних правил работает, когда у правил одинаковая детализация. Это мера того, насколько точно оно описывает подходящие элементы, определяемая числом и видом необходимых аспектов элементов. К примеру, правило для p.a более детально, чем правила для p или просто .a, и будет иметь приоритет над ними.

Запись p > a <…>применима ко всем тегам <a> , находящимся внутри тега <p> и являющимся его прямыми потомками.
Подобным образом p a <…>применимо также ко всем тегам <a> внутри <p> , при этом неважно, является ли <a> прямым потомком или нет.

Селекторы запросов

В этой книге мы не будем часто использовать таблицы стилей. Понимание их работы критично для программирования в браузере, но подробное разъяснение всех их свойств заняло бы 2-3 книги. Главная причина знакомства с ними и с синтаксисом селекторов (записей, определяющих, к каким элементам относятся правила) – мы можем использовать тот же эффективный мини-язык для поиска элементов DOM.

Метод querySelectorAll, существующий и у объекта document, и у элементов-узлов, принимает строку селектора и возвращает массивоподобный объект, содержащий все элементы, подходящие под него.

В отличие от методов вроде getElementsByTagName, возвращаемый querySelectorAll объект не интерактивный. Он не изменится, если вы измените документ.

Метод querySelector (без All) работает сходным образом. Он нужен, если вам необходим один конкретный элемент. Он вернёт только первое совпадение, или null, если совпадений нет.

Расположение и анимация

Свойство стилей position сильно влияет на расположение элементов. По умолчанию оно равно static, что означает, что элемент находится на своём обычном месте в документе. Когда оно равно relative, элемент всё ещё занимает место, но теперь свойства top и left можно использовать для сдвига относительно его обычного расположения. Когда оно равно absolute, элемент удаляется из нормального «потока» документа – то есть, он не занимает место и может накладываться на другие. Кроме того, его свойства left и top можно использовать для абсолютного позиционирования относительно левого верхнего угла ближайшего включающего его элемента, у которого position не равно static. А если такого элемента нет, тогда он позиционируется относительно документа.

Мы можем использовать это для создания анимации. Следующий документ показывает картинку с котом, которая двигается по эллипсу.

Картинка отцентрирована на странице и ей задана position: relative. Мы постоянно обновляем свойства top и left картинки, чтобы она двигалась.

Скрипт использует requestAnimationFrame для вызова функции animate каждый раз, когда браузер готов перерисовывать экран. Функция animate сама опять вызывает requestAnimationFrame, чтобы запланировать следующее обновление. Когда окно браузера (или закладка) активна, это приведёт к обновлениям со скорость примерно 60 раз в секунду, что позволяет добиться хорошо выглядящей анимации.

Если бы мы просто обновляли DOM в цикле, страница бы зависла и ничего не было бы видно. Браузеры не обновляют страницу во время работы JavaScript, и не допускают в это время работы со страницей. Поэтому нам нужна requestAnimationFrame – она сообщает браузеру, что мы пока закончили, и он может заниматься своими браузерными вещами, например обновлять экран и отвечать на запросы пользователя.

Наша функция анимации передаётся текущее время через аргументы, которое оно сравнивает с предыдущим (переменная lastTime), чтобы движение кота было однородным, и анимация работала плавно. Если бы мы просто передвигали её на заданный промежуток на каждом шаге, движение бы запиналось если бы, например, другая задача загрузила бы компьютер.

Движение по кругу осуществляется с применением тригонометрических функций Math.cos и Math.sin. Я кратко опишу их для тех, кто с ними не знаком, так как они понадобятся нам в дальнейшем.

Math.cos и Math.sin полезны тогда, когда надо найти точки на окружности с центром в точке (0, 0) и радиусом в единицу. Обе функции интерпретируют свой аргумент как позицию на окружности, следуя против часовой стрелки, от нуля в самой правой точке, пока путь диной в 2π (около 6.28) не проведёт нас по кругу. Math.cos считает координату по оси x той точки, которая является нашей текущей позицией на окружности, а Math.sin выдаёт координату y. Позиции (или углы) больше, чем 2π или меньше чем 0, тоже допустимы – повороты повторяются так, что a+2π означает тот же самый угол, что и a.


Использование синуса и косинуса для вычисления координат

Анимация кота хранит счётчик angle для текущего угла поворота анимации, и увеличивает его пропорционально прошедшему времени каждый раз при вызове функции animation. Этот угол используется для подсчёта текущей позиции элемента image. Стиль top подсчитывается через Math.sin и умножается на 20 – это вертикальный радиус нашего эллипса. Стиль left считается через Math.cos и умножается на 200, так что ширина эллипса сильно больше высоты.

Стилям обычно требуются единицы измерения. В нашем случае приходится добавлять px к числу, чтобы объяснить браузеру, что мы считаем в пикселях (а не в сантиметрах, ems или других единицах). Это легко забыть. Использование чисел без единиц измерения приведёт к игнорированию стиля – если только число не равно 0, что не зависит от единиц измерения.

Программы JavaScript могут изучать и изменять текущий отображаемый браузером документ через структуру под названием DOM. Эта структура данных представляет модель документа браузера, а программа JavaScript может изменять её для изменения видимого документа. DOM организован в виде дерева, в котором элементы расположены иерархически в соответствии со структурой документа. У объектов элементов есть свойства типа parentNode и childNodes, которые используются для ориентирования на дереве.

Внешний вид документа можно изменять через стили, либо добавляя стили к узлам напрямую, либо определяя правила для каких-либо узлов. У стилей есть очень много свойств, таких, как color или display. JavaScript может влиять на стиль элемента напрямую через его свойство style.

Упражнения
Строим таблицу

Мы строили таблицы из простого текста в главе 6. HTML упрощает построение таблиц. Таблица в HTML строится при помощи следующих тегов:

Для каждой строки в теге <table> содержится тег <tr> . Внутри него мы можем размещать ячейки: либо ячейки заголовков <th> , либо обычные ячейки <td> .

Те же данные, что мы использовали в главе 6, снова доступны в переменной MOUNTAINS.

Напишите функцию buildTable, которая, принимая массив объектов с одинаковыми свойствами, строит структуру DOM, представляющую таблицу. У таблицы должна быть строка с заголовками, где имена свойств обёрнуты в элементы <th> , и должно быть по одной строчке на объект из массива, где его свойства обёрнуты в элементы <td> . Здесь пригодится функция Object.keys, возвращающая массив, содержащий имена свойств объекта.

Когда вы разберётесь с основами, выровняйте ячейки с числами по правому краю, изменив их свойство style.textAlign на «right».

Элементы по имени тегов

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

Чтобы выяснить имя тега элемента, используйте свойство tagName. Заметьте, что оно возвратит имя тега в верхнем регистре. Используйте методы строк toLowerCase или toUpperCase.

Шляпа кота

Расширьте анимацию кота, чтобы и кот и его шляпа <img src="img/hat.png"> летали по противоположным сторонам эллипса.

Или пусть шляпа летает вокруг кота. Или ещё что-нибудь интересное придумайте.

Чтобы упростить расположение множества объектов, неплохо будет переключиться на абсолютное позиционирование. Тогда top и left будут считаться относительно левого верхнего угла документа. Чтобы не использовать отрицательные координаты, вы можете добавить заданное число пикселей к значениям position.

Name already in use

frontend-lection / 6.dom.md

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink
  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

На картинке ниже в общих чертах показано, что доступно для JavaScript в браузерном окружении:

alt

DOM (Document Object Model)

Document Object Model, сокращённо DOM – объектная модель документа, которая представляет все содержимое страницы в виде объектов, которые можно менять.

DOM — описывает структуру документа, манипуляции с контентом и события

BOM (Browser Object Model)

Объектная модель браузера (Browser Object Model, BOM) – это дополнительные объекты, предоставляемые браузером (окружением), чтобы работать со всем, кроме документа

DOM (Document Object Model)

В соответствии с объектной моделью документа («Document Object Model», коротко DOM), каждый HTML-тег является объектом. Вложенные теги являются «детьми» родительского элемента. Текст, который находится внутри тега, также является объектом.

alt

Пробелы, комментарии и переводы строки – это полноправные символы, как буквы и цифры. Они образуют текстовые узлы и становятся частью дерева DOM.

alt

Если браузер сталкивается с некорректно написанным HTML-кодом, он автоматически корректирует его при построении DOM.

Типы Node — NodeType

DOCUMENT_NODE — document – «входная точка» в DOM.

ELEMENT_NODE — HTML-теги, основные строительные блоки.

TEXT_NODE — текстовые узлы – содержат текст.

ATTRIBUTE_NODE — аттрибуты элементов

Навигация по DOM

Дочерние узлы (или дети) – элементы, которые являются непосредственными детьми узла. Другими словами, элементы, которые лежат непосредственно внутри данного. Например, <head> и <body> являются детьми элемента <html> .

Потомки – все элементы, которые лежат внутри данного, включая детей, их детей и т.д.

Навигация по DOM-элементам

alt

section — Дочерние узлы — div, ul

section — Потомки div, span, ul, li, b

Коллекция childNodes содержит список всех детей, включая текстовые узлы.

Свойства firstChild и lastChild обеспечивают быстрый доступ к первому и последнему дочернему элементу.

nextSibling — следующий узел того же родителя (следующий сосед)

previousSibling — предыдущий узел того же родителя (следующий сосед)

Навигация только по элементам

alt

children – коллекция детей, которые являются элементами.

firstElementChild, lastElementChild – первый и последний дочерний элемент.

previousElementSibling, nextElementSibling – соседи-элементы.

children — возвращает HTMLCollection

childNodes — возвращает NodeList

Живые(динамические, HTMLCollection) и неживые(статические, NodeList) коллекции в JavaScript

То есть либо реагируют на любое изменение DOM, либо нет.

Предки элемента – родитель, родитель родителя, его родитель и так далее. Вместе они образуют цепочку иерархии от элемента до вершины.

Атрибуты – это то, что написано в HTML.

Свойства – это то, что находится в DOM-объектах.

Методы для работы с атрибутами:

  • elem.hasAttribute(name) – проверить на наличие.
  • elem.getAttribute(name) – получить значение.
  • elem.setAttribute(name, value) – установить значение.
  • elem.removeAttribute(name) – удалить атрибут.
  • elem.toggleAttribute(name, force) — переключить
  • elem.attributes – это коллекция всех атрибутов.

dataset, Дата аттрибуты

Свойства innerHTML, innerText, textContent

innerHTML — свойство, которое используется для установки или получения разметки элемента.

innerHTML удобно использовать, чтобы быстро очистить содержимое элемента.

Свойство innerHTML не рекомендуется использовать для вставки пользовательских (или непроверенных) данных, так как это потенциальная уязвимость XSS.

innerText — это свойство, которое позволяет задавать или получать текстовое содержимое элемента и его потомков. Метод умеет считывать стили и не возвращает содержимое скрытых элементов.

textContent — это свойство, также как и innerText, позволяет получить текстовый контент элемента и его потомков. Но в отличие от innerText свойство возвращает полный текст, включая то, что содержится в тегах <script> и <style> .

Свойство textContent предназначено для получения и записи только текста, если попытаться записать в него HTML-теги, браузер их не поймёт.

Главные свойства DOM-узла:

Свойство nodeType позволяет узнать тип DOM-узла. Его значение – числовое: 1 для элементов, 3 для текстовых узлов, и т.д. Только для чтения.

Для элементов это свойство возвращает название тега (записывается в верхнем регистре, за исключением XML-режима).

Внутреннее HTML-содержимое узла-элемента. Можно изменять.

Полный HTML узла-элемента. Запись в elem.outerHTML не меняет elem. Вместо этого она заменяет его во внешнем контексте.

Содержимое узла-неэлемента (текст, комментарий).

Текст внутри элемента: HTML за вычетом всех <тегов>.

Когда значение установлено в true, делает то же самое, что и CSS display:none.

Не забудьте добавить к значениям единицы измерения.

  • node.append(. nodes or strings) – добавляет узлы или строки в конец node,
  • node.prepend(. nodes or strings) – вставляет узлы или строки в начало node,
  • node.before(. nodes or strings) — вставляет узлы или строки до node,
  • node.after(. nodes or strings) — вставляет узлы или строки после node,
  • node.replaceWith(. nodes or strings) — заменяет node заданными узлами или строками.
  • node.remove()
  • «beforebegin» – вставить html непосредственно перед elem,
  • «afterbegin» – вставить html в начало elem,
  • «beforeend» – вставить html в конец elem,
  • «afterend» – вставить html непосредственно после elem.

изменение свойств у элемента (это может быть добавление/удаление класса, добавление/удаление атрибута, изменение содержимого элемента и так далее)

innerHTML = «» если нужно

создание элементов ( есди нужно, установка значения атрибутов)

добавление элементов в DOM

Размеры и прокрутка элементов

Размеры и прокрутка окна

Каждый DOM-узел принадлежит соответствующему встроенному классу.

Корнем иерархии является EventTarget, от него наследует Node и остальные DOM-узлы.

alt

EventTarget – это корневой «абстрактный» класс для всего.

Объекты этого класса никогда не создаются. Он служит основой, благодаря которой все DOM-узлы поддерживают так называемые «события».

  • click – происходит, когда кликнули на элемент левой кнопкой мыши (на устройствах с сенсорными экранами оно происходит при касании).
  • contextmenu – происходит, когда кликнули на элемент правой кнопкой мыши.
  • mouseover / mouseout – когда мышь наводится на / покидает элемент.
  • mousedown / mouseup – когда нажали / отжали кнопку мыши на элементе.
  • mousemove – при движении мыши.

События на элементах управления:

  • submit – пользователь отправил форму <form> .
  • focus – пользователь фокусируется на элементе, например нажимает на <input> .
  • blur – когда элемент теряет фокус.
  • change — Событие change срабатывает по окончании изменения элемента.
  • input — срабатывает каждый раз при изменении значения.
  • keydown и keyup – когда пользователь нажимает / отпускает клавишу.
  • DOMContentLoaded – когда HTML загружен и обработан, DOM документа полностью построен и доступен.
  • scroll — позволяет реагировать на прокрутку страницы или элемента.
  • resize — позволяет реагировать на изменение размеров страницы или элемента.
  • transitionend – когда CSS-анимация завершена

addEventListener(event, handler, [options]);

  • event — Имя события, например «click».
  • handler — Ссылка на функцию-обработчик.
  • options — Дополнительный объект со свойствами:
    • once: true — если true, тогда обработчик будет автоматически удалён после выполнения.
    • capture: фаза, на которой должен сработать обработчик, что options может быть false/true, это то же самое, что .
    • passive: если true, то указывает, что обработчик никогда не вызовет preventDefault()

    Всплытие и погружение

    Когда на элементе происходит событие, обработчики сначала срабатывают на нём, потом на его родителе, затем выше и так далее, вверх по цепочке предков.

    Всегда можно узнать, на каком конкретно элементе произошло событие.

    event.target — самый глубокий элемент, который вызывает событие, называется целевым элементом, и он доступен через event.target.

    event.currentTarget(this) — Элемент, на котором сработал обработчик. Значение – обычно такое же, как и у this, но если обработчик является функцией-стрелкой или при помощи bind привязан другой объект в качестве this, то мы можем получить элемент из event.currentTarget.

    event.type — Тип события, в данном случае «click».

    Отличия event.target от event.currentTarget(this): event.target – это «целевой» элемент, на котором произошло событие, в процессе всплытия он неизменен. this – это «текущий» элемент, до которого дошло всплытие, на нём сейчас выполняется обработчик

    Всплытие идёт с «целевого» элемента прямо наверх. По умолчанию событие будет всплывать до элемента <html> , а затем до объекта document, а иногда даже до window, вызывая все обработчики на своём пути.

    Но любой промежуточный обработчик может решить, что событие полностью обработано, и остановить всплытие.

    Для этого нужно вызвать метод event.stopPropagation().

    Если у элемента есть несколько обработчиков на одно событие, то даже при прекращении всплытия все они будут выполнены.

    То есть, event.stopPropagation() препятствует продвижению события дальше, но на текущем элементе все обработчики будут вызваны.

    Для того, чтобы полностью остановить обработку, существует метод event.stopImmediatePropagation(). Он не только предотвращает всплытие, но и останавливает обработку событий на текущем элементе.

    Стандарт DOM Events описывает 3 фазы прохода события:

    • Фаза погружения (capturing phase) – событие сначала идёт сверху вниз.
    • Фаза цели (target phase) – событие достигло целевого(исходного) элемента.
    • Фаза всплытия (bubbling stage) – событие начинает всплывать.

    alt

    При наступлении события – самый глубоко вложенный элемент, на котором оно произошло, помечается как «целевой» (event.target).

    Затем событие сначала двигается вниз от корня документа к event.target, по пути вызывая обработчики, поставленные через addEventListener(. true), где true – это сокращение для .

    Далее событие двигается от event.target вверх к корню документа, по пути вызывая обработчики, поставленные через on <event> и addEventListener без третьего аргумента или с третьим аргументом равным false.

    Каждый обработчик имеет доступ к свойствам события event:

    • event.target – самый глубокий элемент, на котором произошло событие.
    • event.currentTarget (=this) – элемент, на котором в данный момент сработал обработчик (тот, на котором «висит» конкретный обработчик)
    • event.eventPhase – на какой фазе он сработал (погружение=1, фаза цели=2, всплытие=3).

    Любой обработчик может остановить событие вызовом event.stopPropagation(), но делать это не рекомендуется, так как в дальнейшем это событие может понадобиться, иногда для самых неожиданных вещей.

    Идея в том, что если у нас есть много элементов, события на которых нужно обрабатывать похожим образом, то вместо того, чтобы назначать обработчик каждому, мы ставим один обработчик на их общего предка.

    • Упрощает процесс инициализации и экономит память: не нужно вешать много обработчиков.
    • Меньше кода: при добавлении и удалении элементов не нужно ставить или снимать обработчики.
    • Удобство изменений DOM: можно массово добавлять или удалять элементы путём изменения innerHTML и ему подобных.
    • Во-первых, событие должно всплывать. Некоторые события этого не делают. Также, низкоуровневые обработчики не должны вызывать event.stopPropagation().
    • Во-вторых, делегирование создаёт дополнительную нагрузку на браузер, ведь обработчик запускается, когда событие происходит в любом месте контейнера, не обязательно на элементах, которые нам интересны. Но обычно эта нагрузка настолько пустяковая, что её даже не стоит принимать во внимание.

    Действия браузера по умолчанию

    • Основной способ – это воспользоваться объектом event. Для отмены действия браузера существует стандартный метод event.preventDefault().
    • Если же обработчик назначен через on<событие> (не через addEventListener), то также можно вернуть false из обработчика.

    События, вытекающие из других

    Некоторые события естественным образом вытекают друг из друга. Если мы отменим первое событие, то последующие не возникнут.

    mousedown/mouseup — Кнопка мыши нажата/отпущена над элементом.

    mouseover/mouseout — Курсор мыши появляется над элементом и уходит с него.

    mousemove — Каждое движение мыши над элементом генерирует это событие.

    click — Вызывается при mousedown , а затем mouseup над одним и тем же элементом, если использовалась левая кнопка мыши.

    dblclick — Вызывается двойным кликом на элементе.

    Например, клик мышью вначале вызывает mousedown, когда кнопка нажата, затем mouseup и click, когда она отпущена.

    Опция «passive» для обработчика

    Необязательная опция passive: true для addEventListener сигнализирует браузеру, что обработчик не собирается выполнять preventDefault().

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

    Поэтому, когда браузер обнаружит такое событие, он должен для начала запустить все обработчики и после, если preventDefault не вызывается нигде, он может начать прокрутку. Это может вызвать ненужные задержки в пользовательском интерфейсе.

    Опция passive: true сообщает браузеру, что обработчик не собирается отменять прокрутку. Тогда браузер начинает её немедленно, обеспечивая максимально плавный интерфейс, параллельно обрабатывая событие.

    Свойство event.defaultPrevented установлено в true, если действие по умолчанию было предотвращено, и false, если нет.

    вместо event.stopPropagation(), можно исп. в опре. сценариях event.defaultPrevented

    было ли отменено действие по умолчанию?

    Встроенные классы для событий формируют иерархию аналогично классам для DOM-элементов. Её корнем является встроенный класс Event.

    Событие встроенного класса Event можно создать так:

    let event = new Event(type[, options]);

    type – тип события, строка, например «click» или же любой придуманный нами – «my-event».

    options – объект с тремя необязательными свойствами:

    • bubbles: true/false – если true, тогда событие всплывает.
    • cancelable: true/false – если true, тогда можно отменить действие по умолчанию.
    • composed: true/false – если true, тогда событие будет всплывать наружу за пределы Shadow DOM.

    После того, как объект события создан, мы должны запустить его на элементе, вызвав метод elem.dispatchEvent(event).

    • UIEvent
    • FocusEvent
    • MouseEvent
    • WheelEvent
    • KeyboardEvent

    Технически CustomEvent абсолютно идентичен Event за исключением одной небольшой детали.

    У второго аргумента-объекта есть дополнительное свойство detail, в котором можно указывать информацию для передачи в событие.

    Вложенные события обрабатываются синхронно, обычно события обрабатываются асинхронно.

    Формы, элементы управления

    Событие change срабатывает по окончании изменения элемента.

    Для текстовых <input> это означает, что событие происходит при потере фокуса.

    Для других элементов: select, input type=checkbox/radio событие запускается сразу после изменения значения:

    Событие input срабатывает каждый раз при изменении значения.

    Есть два основных способа отправить форму:

    • Первый – нажать кнопку <input type=»submit»>, <button type=»submit»> .
    • Второй – нажать Enter, находясь на каком-нибудь поле

    Оба действия сгенерируют событие submit на форме. Обработчик может проверить данные, и если есть ошибки, показать их и вызвать event.preventDefault(), тогда форма не будет отправлена на сервер.

    Метод: submit, reset

    Метод form.submit() позволяет инициировать отправку формы из JavaScript. Мы можем использовать его для динамического создания и отправки наших собственных форм на сервер.

    При этом событие submit не генерируется. Предполагается, что если программист вызывает метод form.submit(), то он уже выполнил всю соответствующую обработку.

    Страница: DOMContentLoaded, load, beforeunload, unload

    DOMContentLoaded – браузер полностью загрузил HTML, было построено DOM-дерево, но внешние ресурсы, такие как картинки <img> и стили, могут быть ещё не загружены.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *