diff --git a/2ech13.md b/3ech14.md
index 2f8209b..6081ee9 100644
--- a/2ech13.md
+++ b/3ech14.md
@@ -1,12 +1,16 @@
-# Chapter 13The Document Object Model
+# Chapter 14The Document Object Model
-When you open a web page in your browser, the browser retrieves the page's HTML text and parses it, much like the way our parser from [Chapter 11](11_language.html#parsing) parsed programs. The browser builds up a model of the document's structure and then uses this model to draw the page on the screen.
+> Too bad! Same old story! Once you've finished building your house you notice you've accidentally learned something that you really should have known—before you started.
+>
+> <footer>Friedrich Nietzsche, <cite>Beyond Good and Evil</cite></footer>
-This representation of the document is one of the toys that a JavaScript program has available in its sandbox. You can read from the model and also change it. It acts as a _live_ data structure: when it is modified, the page on the screen is updated to reflect the changes.
+When you open a web page in your browser, the browser retrieves the page's HTML text and parses it, much like the way our parser from [Chapter 12](12_language.html#parsing) parsed programs. The browser builds up a model of the document's structure and uses this model to draw the page on the screen.
+
+This representation of the document is one of the toys that a JavaScript program has available in its sandbox. It is a data structure that you can read or modify. It acts as a _live_ data structure: when it's modified, the page on the screen is updated to reflect the changes.
## Document structure
-You can imagine an HTML document as a nested set of boxes. Tags such as `<body>` and `</body>` enclose other tags, which in turn contain other tags or text. Here's the example document from the [previous chapter](12_browser.html#browser):
+You can imagine an HTML document as a nested set of boxes. Tags such as `<body>` and `</body>` enclose other tags, which in turn contain other tags or text. Here's the example document from the [previous chapter](13_browser.html):
```
@@ -25,62 +29,65 @@ You can imagine an HTML document as a nested set of boxes. Tags such as `<bod
This page has the following structure:
-
+
My ostrich Gertrude:
One
@@ -131,20 +140,20 @@ Almost everything about the DOM data structure can be changed. Element nodes havThree
``` -A node can exist in the document in only one place. Thus, inserting paragraph “Three” in front of paragraph “One” will first remove it from the end of the document and then insert it at the front, resulting in “Three/One/Two”. All operations that insert a node somewhere will, as a side effect, cause it to be removed from its current position (if it has one). +A node can exist in the document in only one place. Thus, inserting paragraph _Three_ in front of paragraph _One_ will first remove it from the end of the document and then insert it at the front, resulting in _Three_/_One_/_Two_. All operations that insert a node somewhere will, as a side effect, cause it to be removed from its current position (if it has one). The `replaceChild` method is used to replace a child node with another one. It takes as arguments two nodes: a new node and the node to be replaced. The replaced node must be a child of the element the method is called on. Note that both `replaceChild` and `insertBefore` expect the _new_ node as their first argument. ## Creating nodes -In the following example, we want to write a script that replaces all images (`<img>` tags) in the document with the text held in their `alt` attributes, which specifies an alternative textual representation of the image. +Say we want to write a script that replaces all images (`<img>` tags) in the document with the text held in their `alt` attributes, which specifies an alternative textual representation of the image. -This involves not only removing the images but adding a new text node to replace them. For this, we use the `document.createTextNode` method. +This involves not only removing the images but adding a new text node to replace them. Text nodes are created with the `document.<wbr>createTextNode` method. ```The in the
@@ -154,11 +163,11 @@ This involves not only removing the images but adding a new text node to replace
```
-Given a string, `createTextNode` gives us a type 3 DOM node (a text node), which we can insert into the document to make it show up on the screen.
+Given a string, `createTextNode` gives us a text node, which we can insert into the document to make it show up on the screen.
-The loop that goes over the images starts at the end of the list of nodes. This is necessary because the node list returned by a method like `getElementsByTagName` (or a property like `childNodes`) is _live_. That is, it is updated as the document changes. If we started from the front, removing the first image would cause the list to lose its first element so that the second time the loop repeats, where `i` is 1, it would stop because the length of the collection is now also 1.
+The loop that goes over the images starts at the end of the list. This is necessary because the node list returned by a method like `getElementsByTagName` (or a property like `childNodes`) is _live_. That is, it is updated as the document changes. If we started from the front, removing the first image would cause the list to lose its first element so that the second time the loop repeats, where `i` is 1, it would stop because the length of the collection is now also 1.
-If you want a _solid_ collection of nodes, as opposed to a live one, you can convert the collection to a real array by calling the array `slice` method on it.
+If you want a _solid_ collection of nodes, as opposed to a live one, you can convert the collection to a real array by calling `Array.from`.
```
-var arrayish = {0: "one", 1: "two", length: 2};
-var real = Array.prototype.slice.call(arrayish, 0);
-real.forEach(function(elt) { console.log(elt); });
-// → one
-// two
+let arrayish = {0: "one", 1: "two", length: 2};
+let array = Array.from(arrayish);
+console.log(array.map(s => s.toUpperCase()));
+// → ["ONE", "TWO"]
```
-To create regular element nodes (type 1), you can use the `document.createElement` method. This method takes a tag name and returns a new empty node of the given type.
+To create element nodes, you can use the `document.<wbr>createElement` method. This method takes a tag name and returns a new empty node of the given type.
-The following example defines a utility `elt`, which creates an element node and treats the rest of its arguments as children to that node. This function is then used to add a simple attribution to a quote.
+The following example defines a utility `elt`, which creates an element node and treats the rest of its arguments as children to that node. This function is then used to add an attribution to a quote.
```
@@ -192,13 +200,11 @@ The following example defines a utility `elt`, which creates an element node and-``` - -I recommended prefixing the names of such made-up attributes with `data-` to ensure they do not conflict with any other attributes. - -As a simple example, we'll write a “syntax highlighter” that looks for `<pre>` tags (“preformatted”, used for code and similar plaintext) with a `data-language` attribute and crudely tries to highlight the keywords for that language. - -``` -function highlightCode(node, keywords) { - var text = node.textContent; - node.textContent = ""; // Clear the node - - var match, pos = 0; - while (match = keywords.exec(text)) { - var before = text.slice(pos, match.index); - node.appendChild(document.createTextNode(before)); - var strong = document.createElement("strong"); - strong.appendChild(document.createTextNode(match[0])); - node.appendChild(strong); - pos = keywords.lastIndex; - } - var after = text.slice(pos); - node.appendChild(document.createTextNode(after)); -} -``` - -The function `highlightCode` takes a `<pre>` node and a regular expression (with the “global” option turned on) that matches the keywords of the programming language that the element contains. - -The `textContent` property is used to get all the text in the node and is then set to an empty string, which has the effect of emptying the node. We loop over all matches of the keyword expression, appending the text _between_ them as regular text nodes, and the text matched (the keywords) as text nodes wrapped in `<strong>` (bold) elements. - -We can automatically highlight all programs on the page by looping over all the `<pre>` elements that have a `data-language` attribute and calling `highlightCode` on each one with the correct regular expression for the language. - -``` -var languages = { - javascript: /\b(function|return|var)\b/g /* … etc */ -}; - -function highlightAllCode() { - var pres = document.body.getElementsByTagName("pre"); - for (var i = 0; i < pres.length; i++) { - var pre = pres[i]; - var lang = pre.getAttribute("data-language"); - if (languages.hasOwnProperty(lang)) - highlightCode(pre, languages[lang]); + let paras = document.body.getElementsByTagName("p"); + for (let para of Array.from(paras)) { + if (para.getAttribute("data-classified") == "secret") { + para.remove(); + } } -} -``` - -Here is an example: - + ``` -
Here it is, the identity function:
--function id(x) { return x; } -- -``` +It is recommended to prefix the names of such made-up attributes with `data-` to ensure they do not conflict with any other attributes. -There is one commonly used attribute, `class`, which is a reserved word in the JavaScript language. For historical reasons—some old JavaScript implementations could not handle property names that matched keywords or reserved words—the property used to access this attribute is called `className`. You can also access it under its real name, `"class"`, by using the `getAttribute` and `setAttribute` methods. +There is a commonly used attribute, `class`, which is a keyword in the JavaScript language. For historical reasons—some old JavaScript implementations could not handle property names that matched keywords—the property used to access this attribute is called `className`. You can also access it under its real name, `"class"`, by using the `getAttribute` and `setAttribute` methods. ## Layout -You might have noticed that different types of elements are laid out differently. Some, such as paragraphs (`<p>`) or headings (`<h1>`), take up the whole width of the document and are rendered on separate lines. These are called _block_ elements. Others, such as links (`<a>`) or the `<strong>` element used in the previous example, are rendered on the same line with their surrounding text. Such elements are called _inline_ elements. +You may have noticed that different types of elements are laid out differently. Some, such as paragraphs (`<p>`) or headings (`<h1>`), take up the whole width of the document and are rendered on separate lines. These are called _block_ elements. Others, such as links (`<a>`) or the `<strong>` element, are rendered on the same line with their surrounding text. Such elements are called _inline_ elements. For any given document, browsers are able to compute a layout, which gives each element a size and position based on its type and content. This layout is then used to actually draw the document. -The size and position of an element can be accessed from JavaScript. The `offsetWidth` and `offsetHeight` properties give you the space the element takes up in _pixels_. A pixel is the basic unit of measurement in the browser and typically corresponds to the smallest dot that your screen can display. Similarly, `clientWidth` and `clientHeight` give you the size of the space _inside_ the element, ignoring border width. +The size and position of an element can be accessed from JavaScript. The `offsetWidth` and `offsetHeight` properties give you the space the element takes up in _pixels_. A pixel is the basic unit of measurement in the browser. It traditionally corresponds to the smallest dot that the screen can draw, but on modern displays, which can draw _very_ small dots, that may no longer be the case, and a browser pixel may span multiple display dots. + +Similarly, `clientWidth` and `clientHeight` give you the size of the space _inside_ the element, ignoring border width. ```
@@ -303,17 +258,17 @@ The size and position of an element can be accessed from JavaScript. The `offset
``` -The most effective way to find the precise position of an element on the screen is the `getBoundingClientRect` method. It returns an object with `top`, `bottom`, `left`, and `right` properties, indicating the pixel positions of the sides of the element relative to the top left of the screen. If you want them relative to the whole document, you must add the current scroll position, found under the global `pageXOffset` and `pageYOffset` variables. +The most effective way to find the precise position of an element on the screen is the `getBoundingClientRect` method. It returns an object with `top`, `bottom`, `left`, and `right` properties, indicating the pixel positions of the sides of the element relative to the top left of the screen. If you want them relative to the whole document, you must add the current scroll position, which you can find in the `pageXOffset` and `pageYOffset` bindings. -Laying out a document can be quite a lot of work. In the interest of speed, browser engines do not immediately re-layout a document every time it is changed but rather wait as long as they can. When a JavaScript program that changed the document finishes running, the browser will have to compute a new layout in order to display the changed document on the screen. When a program _asks_ for the position or size of something by reading properties such as `offsetHeight` or calling `getBoundingClientRect`, providing correct information also requires computing a layout. +Laying out a document can be quite a lot of work. In the interest of speed, browser engines do not immediately re-layout a document every time you change it, but wait as long as they can. When a JavaScript program that changed the document finishes running, the browser will have to compute a new layout in order to draw the changed document to the screen. When a program _asks_ for the position or size of something by reading properties such as `offsetHeight` or calling `getBoundingClientRect`, providing correct information also requires computing a layout. -A program that repeatedly alternates between reading DOM layout information and changing the DOM forces a lot of layouts to happen and will consequently run really slowly. The following code shows an example of this. It contains two different programs that build up a line of _X_ characters 2,000 pixels wide and measures the time each one takes. +A program that repeatedly alternates between reading DOM layout information and changing the DOM forces a lot of layout computations to happen and will consequently run very slowly. The following code is an example of this. It contains two different programs that build up a line of _X_ characters 2,000 pixels wide and measures the time each one takes. ```@@ -321,24 +276,24 @@ A program that repeatedly alternates between reading DOM layout information and @@ -346,9 +301,9 @@ A program that repeatedly alternates between reading DOM layout information and ## Styling -We have seen that different HTML elements display different behavior. Some are displayed as blocks, others inline. Some add styling, such as `<strong>` making its content bold and `<a>` making it blue and underlining it. +We have seen that different HTML elements are drawn differently. Some are displayed as blocks, others inline. Some add styling—`<strong>` makes its content bold and `<a>` makes it blue and underlines it. -The way an `<img>` tag shows an image or an `<a>` tag causes a link to be followed when it is clicked is strongly tied to the element type. But the default styling associated with an element, such as the text color or underline, can be changed by us. Here is an example using the `style` property: +The way an `<img>` tag shows an image or an `<a>` tag causes a link to be followed when it is clicked is strongly tied to the element type. But the default styling associated with an element, such as the text color or underline, can be changed by us. Here is an example that uses the `style` property: ``` @@ -357,7 +312,7 @@ The way an `<img>` tag shows an image or an `<a>` tag causes a link A style attribute may contain one or more _declarations_, which are a property (such as `color`) followed by a colon and a value (such as `green`). When there is more than one declaration, they must be separated by semicolons, as in `"color: red; border: none"`. -There are a lot of aspects that can be influenced by styling. For example, the `display` property controls whether an element is displayed as a block or an inline element. +There are a lot of aspects of the document that can be influenced by styling. For example, the `display` property controls whether an element is displayed as a block or an inline element. ``` This text is displayed inline, @@ -365,23 +320,23 @@ This text is displayed inline, not at all. ``` -The `block` tag will end up on its own line since block elements are not displayed inline with the text around them. The last tag is not displayed at all—`display: none` prevents an element from showing up on the screen. This is a way to hide elements. It is often preferable to removing them from the document entirely because it makes it easy to reveal them again at a later time. +The `block` tag will end up on its own line since block elements are not displayed inline with the text around them. The last tag is not displayed at all—`display: none` prevents an element from showing up on the screen. This is a way to hide elements. It is often preferable to removing them from the document entirely because it makes it easy to reveal them again later. -JavaScript code can directly manipulate the style of an element through the node's `style` property. This property holds an object that has properties for all possible style properties. The values of these properties are strings, which we can write to in order to change a particular aspect of the element's style. +JavaScript code can directly manipulate the style of an element through the element's `style` property. This property holds an object that has properties for all possible style properties. The values of these properties are strings, which we can write to in order to change a particular aspect of the element's style. ```
- Pretty text + Nice text
``` -Some style property names contain dashes, such as `font-family`. Because such property names are awkward to work with in JavaScript (you'd have to say `style["font-family"]`), the property names in the `style` object for such properties have their dashes removed and the letters that follow them capitalized (`style.fontFamily`). +Some style property names contain dashes, such as `font-family`. Because such property names are awkward to work with in JavaScript (you'd have to say `style["font-family"]`), the property names in the `style` object for such properties have their dashes removed and the letters after them capitalized (`style.fontFamily`). ## Cascading styles @@ -397,11 +352,11 @@ The styling system for HTML is called CSS for _Cascading Style Sheets_. A _styleNow strong text is italic and gray.
``` -The _cascading_ in the name refers to the fact that multiple such rules are combined to produce the final style for an element. In the previous example, the default styling for `<strong>` tags, which gives them `font-weight: bold`, is overlaid by the rule in the `<style>` tag, which adds `font-style` and `color`. +The _cascading_ in the name refers to the fact that multiple such rules are combined to produce the final style for an element. In the example, the default styling for `<strong>` tags, which gives them `font-weight: bold`, is overlaid by the rule in the `<style>` tag, which adds `font-style` and `color`. -When multiple rules define a value for the same property, the most recently read rule gets a higher precedence and wins. So if the rule in the `<style>` tag included `font-weight: normal`, conflicting with the default `font-weight` rule, the text would be normal, _not_ bold. Styles in a `style` attribute applied directly to the node have the highest precedence and always win. +When multiple rules define a value for the same property, the most recently read rule gets a higher precedence and wins. So if the rule in the `<style>` tag included `font-weight: normal`, contradicting the default `font-weight` rule, the text would be normal, _not_ bold. Styles in a `style` attribute applied directly to the node have the highest precedence and always win. -It is possible to target things other than tag names in CSS rules. A rule for `.abc` applies to all elements with `"abc"` in their class attributes. A rule for `#xyz` applies to the element with an `id` attribute of `"xyz"` (which should be unique within the document). +It is possible to target things other than tag names in CSS rules. A rule for `.abc` applies to all elements with `"abc"` in their `class` attribute. A rule for `#xyz` applies to the element with an `id` attribute of `"xyz"` (which should be unique within the document). ``` .subtle { @@ -412,19 +367,19 @@ It is possible to target things other than tag names in CSS rules. A rule for `. background: blue; color: white; } -/* p elements, with classes a and b, and id main */ -p.a.b#main { +/* p elements with id main and with classes a and b */ +p#main.a.b { margin-bottom: 20px; } ``` -The precedence rule favoring the most recently defined rule holds true only when the rules have the same _specificity_. A rule's specificity is a measure of how precisely it describes matching elements, determined by the number and kind (tag, class, or ID) of element aspects it requires. For example, a rule that targets `p.a` is more specific than rules that target `p` or just `.a`, and would thus take precedence over them. +The precedence rule favoring the most recently defined rule applies only when the rules have the same _specificity_. A rule's specificity is a measure of how precisely it describes matching elements, determined by the number and kind (tag, class, or ID) of element aspects it requires. For example, a rule that targets `p.a` is more specific than rules that target `p` or just `.a`, and would thus take precedence over them. The notation `p > a {…}` applies the given styles to all `<a>` tags that are direct children of `<p>` tags. Similarly, `p a {…}` applies to all `<a>` tags inside `<p>` tags, whether they are direct or indirect children. ## Query selectors -We won't be using style sheets all that much in this book. Although understanding them is crucial to programming in the browser, properly explaining all the properties they support and the interaction among those properties would take two or three books. +We won't be using style sheets all that much in this book. Understanding them is helpful when programming in the browser, but they are complicated enough to warrant a separate book. The main reason I introduced _selector_ syntax—the notation used in style sheets to determine which elements a set of styles apply to—is that we can use this same mini-language as an effective way to find DOM elements. @@ -453,73 +408,75 @@ The `querySelectorAll` method, which is defined both on the `document` object an ``` -Unlike methods such as `getElementsByTagName`, the object returned by `querySelectorAll` is _not_ live. It won't change when you change the document. +Unlike methods such as `getElementsByTagName`, the object returned by `querySelectorAll` is _not_ live. It won't change when you change the document. It is still not a real array, though, so you still need to call `Array.from` if you want to treat it like one. -The `querySelector` method (without the `All` part) works in a similar way. This one is useful if you want a specific, single element. It will return only the first matching element or null if no elements match. +The `querySelector` method (without the `All` part) works in a similar way. This one is useful if you want a specific, single element. It will return only the first matching element or null when no element matches. ## Positioning and animating -The `position` style property influences layout in a powerful way. By default it has a value of `static`, meaning the element sits in its normal place in the document. When it is set to `relative`, the element still takes up space in the document, but now the `top` and `left` style properties can be used to move it relative to its normal place. When `position` is set to `absolute`, the element is removed from the normal document flow—that is, it no longer takes up space and may overlap with other elements. Also, its `top` and `left` properties can be used to absolutely position it relative to the top-left corner of the nearest enclosing element whose `position` property isn't `static`, or relative to the document if no such enclosing element exists. +The `position` style property influences layout in a powerful way. By default it has a value of `static`, meaning the element sits in its normal place in the document. When it is set to `relative`, the element still takes up space in the document, but now the `top` and `left` style properties can be used to move it relative to that normal place. When `position` is set to `absolute`, the element is removed from the normal document flow—that is, it no longer takes up space and may overlap with other elements. Also, its `top` and `left` properties can be used to absolutely position it relative to the top-left corner of the nearest enclosing element whose `position` property isn't `static`, or relative to the document if no such enclosing element exists. -We can use this to create an animation. The following document displays a picture of a cat that floats around in an ellipse: +We can use this to create an animation. The following document displays a picture of a cat that moves around in an ellipse: ```
name | height | -country | +place |
---|---|---|---|
Kilimanjaro | @@ -531,42 +488,45 @@ We built plaintext tables in [Chapter 6](06_object.html#tables). HTML makes layi For each _row_, the `<table>` tag contains a `<tr>` tag. Inside of these `<tr>` tags, we can put cell elements: either heading cells (`<th>`) or regular cells (`<td>`). -The same source data that was used in [Chapter 6](06_object.html#mountains) is again available in the `MOUNTAINS` variable in the sandbox. It can also be [downloaded](http://eloquentjavascript.net/2nd_edition/code/mountains.js) from the website. +Given a data set of mountains, an array of objects with `name`, `height`, and `place` properties, generate the DOM structure for a table that enumerates the objects. It should have one column per key and one row per object, plus a header row with `<th>` elements at the top, listing the column names. -Write a function `buildTable` that, given an array of objects that all have the same set of properties, builds up a DOM structure representing a table. The table should have a header row with the property names wrapped in `<th>` elements and should have one subsequent row per object in the array, with its property values in `<td>` elements. +Write this so that the columns are automatically derived from the objects, by taking the property names of the first object in the data. -The `Object.keys` function, which returns an array containing the property names that an object has, will probably be helpful here. +Add the resulting table to the element with an `id` attribute of `"mountains"`, so that it becomes visible in the document. -Once you have the basics working, right-align cells containing numbers by setting their `style.textAlign` property to `"right"`. +Once you have this working, right-align cells that contain number values by setting their `style.textAlign` property to `"right"`. ``` - +