You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

571 lines
47 KiB

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My Learning Website</title>
<link href="/styles/styles.css" rel="stylesheet" type="text/css">
<link href="/webdevelopment/styles/styles.css" rel="stylesheet" type="text/css">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<article>
<h1 class="coursetitle">JavaScript Essential Training</h1>
<h2 class="lecturer">LinkedIn Learning : Morten Rand-Hendriksen</h2>
<h1 class="episodetitle">JavaScript and the DOM, Part 1: Changing DOM Elements</h1>
<h2 class="sectiontitle">DOM: The Document Object Model</h2>
<p>JavaScript itself, doesn't really seem to be particularly object-oriented. It seems to be more of a case that you can create and use objects in JavaScript but you don't have to.</p>
<p>However, that could be a little bit misleading because, sometimes, you are using objects without realising it.</p>
<p>For example, the browser, itself, is actually an object as if everything inside the browser. That is, the browser could be seen as an object hierarchy, with the browser at the root of that tree. This is referred to as the Browser Object Model (BOM).</p>
<p>So, apart from the browser, the BOM includes objects such as a URL, the navigation buttons, the URL, the window and the document.</p>
<p>Like any objects, the objects in the BOM have methods which act as an interface allowing you to manipulate them or return information relating to them. A good example of this is the window. We can use a method to determine the width of the window</p>
<pre><code>
window.innerwidth
</code></pre>
<p>or open a new window with the open method.</p>
<pre><code>
window.open()
</code></pre>
<p>In JavaScript, you probably won't be interacting with the objects of the BOM very often, although it is useful to know about it. There is plenty of information available on the web including</p>
<p><a href="https://www.javascripttutorial.net/javascript-bom/">https://www.javascripttutorial.net/javascript-bom/</a> - this provides some info on the different objects in the BOM.</p>
<p><a href="https://www.w3schools.com/js/js_window.asp">https://www.w3schools.com/js/js_window.asp</a> - perhaps provides a little more detail than the above.</p>
<p><a href="https://www.javatpoint.com/browser-object-model">https://www.javatpoint.com/browser-object-model</a> - like the W3 Schools page, this is a short overview but it also provides individual pages for different objects.</p>
<p>The above 3 also look like pretty good JavaScript sites.</p>
<p>The one object in the BOM that you will interact with regularly is the document. Like the browser, we can think of the document as being at the head of an object hierarchy which we refer to as the document object model or DOM.</p>
<p>Since the document is a child object of window, we can reference it as window.document. In JavaScript, our code will normally be running inside a window so we can reference the document simply as document.</p>
<p>Some useful sources of the DOM include</p>
<p><a href="https://www.w3schools.com/js/js_htmldom.asp">https://www.w3schools.com/js/js_htmldom.asp</a> - again, this does look as though it is quite detailed.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction">https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction</a> - this is the Mozilla page and also looks like it is quite detailed.</p>
<p><a href="https://www.freecodecamp.org/news/whats-the-document-object-model-and-why-you-should-know-how-to-use-it-1a2d0bc5429d/">https://www.freecodecamp.org/news/whats-the-document-object-model-and-why-you-should-know-how-to-use-it-1a2d0bc5429d/</a> - more of a tutorial than a reference.</p>
<p>So, we said that the browser is an object and everything inside it is an object, including a document and this was described by the BOM.</p>
<p>Similarly, everything inside a document is an object, described by the DOM.</p>
<p>The objects inside the DOM are HTML tags. The HTML tag encompasses the root element and inside of this we have two objects, the head and the body.</p>
<p>Both of these containing other HTML elements such as &lt;title&gt; in the head or &lt;h1&gt;, &lt;p&gt; and &lt;a&gt; tags (along with many others) in the body.</p>
<p>We generally think of the DOM (and also the BOM) as having a tress structure like that shown in figure 63.</p>
<img src="images/image35.gif" alt="Figure 63 image">
<p class =>Figure 63 - the DOM shown in a tree structure (image taken from w3schools.com)</p>
<p>However, we could also think of it in terms of containers. That is, the HTML file contains a head and a body. The head will usually contain a title and other metadata. The body contains the elements you see in the browser window.</p>
<p>An example of this is a link or &lt;a&gt; tag. If you write some CSS code to target &lt;a&gt; tags, for example, you are treating this is though it were an object and so we can target all elements that are of the same object class. I guess you could think of this as targeting an element type, like &lt;a&gt;, &lt;p&gt; or &lt;h1&gt; for instance is like a class method and targeting a particular id, which only targets a single element is like an instance method.</p>
<p>If you want to view the code version of the DOM, you can do that in Developer Tools under the Elements tab in Chrome or the Inspect tab in Firefox.</p>
<h1 class="sectiontitle">Target Elements in the DOM with querySelector Methods</h1>
<p>So, we have seen that all of the elements in the document are part of the DOM and can be represented as nodes.</p>
<p>Some of these nodes such as body, title, URL and so on can be thought of as being properties of the document. These are high-level elements and they can be retrieved with standard dot notation.</p>
<ul>
<li>document.body returns the body</li>
<li>document.title returns the title</li>
<li>document.URL returns the URL of the document and so on</li>
</ul>
<p>If we want to return other elements, we can generally do that by using their ID, class name or tag name.</p>
<ul>
<li>document.getElementyByID("some-ID"); returns all elements with that ID</li>
<li>document.getElementyByClassName("classname"); returns all elements with that class name as an array</li>
<li>document.getElementyByTagName("HTML tag"); returns all elements with that HTML tag as an array</li>
</ul>
<p>These are document methods, as the syntax shows. More recent versions of JavaScript have a couple of additional and very useful methods and these querySelector and querySelectorAll.</p>
<ul>
<li>document.querySelector(".main-nav a"); returns the first element that matches the specified selector or selectors</li>
<li>document.querySelectorAll(",post-content p"); returns all elements that match the specified selector or selectors</li>
</ul>
<p>These examples both have a single CSS selector, but we could also use a list of CSS selectors separated by commas. So, we are targeting elements within the document in exactly the same way we would use to target them with a style rule. They are easy to work with and powerful.</p>
<p>To see how this works, the exercise files for this video includes a website so we will open the index.html page and go to the console (in the Web Developer tools).</p>
<p>In the console, we will type</p>
<pre><code>
document.querySelector(".masthead");
</code></pre>
<p>This returns</p>
<pre><code>
1. &lt;div class="site-header centered"&gt;
2. &lt;div class="site-branding"&gt;
3. &lt;h1 class="site-title"&gt;&lt;a href="/"&gt;&lt;span class="icon-mmlogo" data-grunticon-embed=""&gt;&lt;/span&gt;Moonwalk Manor&lt;/a&gt;&lt;/h1&gt;
4. &lt;/div&gt;&lt;!-- .site-title --&gt;
5. &lt;nav id="multi-level-nav" class="menu multi-level-nav"&gt;
6. &lt;ul&gt;
7. &lt;li&gt;&lt;a href="#"&gt;Home&lt;/a&gt;&lt;/li&gt;
8. &lt;li class="has-children"&gt;
9. &lt;a href="#"&gt;Your Stay&lt;button class="dropdown-toggle" aria-expanded="false"&gt;&lt;span class="screen-reader-text"&gt;Expand child menu&lt;/span&gt;&lt;/button&gt;&lt;/a&gt;
10. &lt;ul class="sub-menu"&gt;
11. &lt;li&gt;&lt;a href="#"&gt;Experiences&lt;/a&gt;&lt;/li&gt;
12. &lt;li&gt;&lt;a href="#"&gt;Dining&lt;/a&gt;&lt;/li&gt;
13. &lt;li&gt;&lt;a href="#"&gt;Amenities&lt;/a&gt;&lt;/li&gt;
14. &lt;/ul&gt;
15. &lt;/li&gt;
16. &lt;li&gt;&lt;a href="#"&gt;About&lt;/a&gt;&lt;/li&gt;
17. &lt;li&gt;&lt;a href="#"&gt;Contact&lt;/a&gt;&lt;/li&gt;
18. &lt;li&gt;&lt;a class="reserve" href="#"&gt;Make a Reservation&lt;/a&gt;&lt;/li&gt;
19. &lt;/ul&gt;
20. &lt;/nav&gt;&lt;!-- #multi-level-nav .multi-level-nav --&gt;
21. &lt;/div&gt;
</code></pre>
<p class="caption">Figure 64 - the masthead element returned by our querySelector</p>
<p>The element shown in figure 64 is expanded so we can see the entire element. It may be worth noting that the developer tools give you a lot of options. For example, in Chrome you can right-click the element in the Console and select Copy and then Copy Element which is how we get the output shown in figure 64. These options can differ between browsers.</p>
<p>So, in this we way we can target an element such as the masthead. Note that we are using a CSS selector and so we used a dot. If we were targeting an ID, we would use a hash instead.</p>
<p>As another example, let’s say we want to get all anchor tags in the document. We will use querySelectorAll with the HTML tag.</p>
<pre><code>
document.querySelectorAll("a")
</code></pre>
<p>This returns</p>
<pre><code>
NodeList(50) [a.skip-link.screen-reader-text, a, a, a, a, a, a, a, a, a.reserve, a, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a.content-button, a.content-button, a.content-button, a.content-button, a.content-button, a, a.flex-active, a, a, a.flex-prev, a.flex-next, a, a, a, a, a, a, a, a.icon-linkedin, a.icon-twitter, a.icon-facebook, a.icon-instagram, a.icon-youtube]
</code></pre>
<p>As before, we can expand this as shown in figure 65.</p>
<pre><code>
document.querySelectorAll("a")
1. NodeList(50) [a.skip-link.screen-reader-text, a, a, a, a, a, a, a, a, a.reserve, a, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a, a.content-button, a.content-button, a.content-button, a.content-button, a.content-button, a.content-button, a, a.flex-active, a, a, a.flex-prev, a.flex-next, a, a, a, a, a, a, a, a.icon-linkedin, a.icon-twitter, a.icon-facebook, a.icon-instagram, a.icon-youtube]
1. 0: a.skip-link.screen-reader-text
2. 1: a
3. 2: a
4. 3: a
5. 4: a
6. 5: a
7. 6: a
8. 7: a
9. 8: a
10. 9: a.reserve
11. 10: a
12. 11: a
13. 12: a.content-button
14. 13: a
15. 14: a.content-button
16. 15: a
17. 16: a.content-button
18. 17: a
19. 18: a.content-button
20. 19: a
21. 20: a.content-button
22. 21: a
23. 22: a.content-button
24. 23: a
25. 24: a.content-button
26. 25: a
27. 26: a.content-button
28. 27: a.content-button
29. 28: a.content-button
30. 29: a.content-button
31. 30: a.content-button
32. 31: a.content-button
33. 32: a
34. 33: a.flex-active
35. 34: a
36. 35: a
37. 36: a.flex-prev
38. 37: a.flex-next
39. 38: a
40. 39: a
41. 40: a
42. 41: a
43. 42: a
44. 43: a
45. 44: a
46. 45: a.icon-linkedin
47. 46: a.icon-twitter
48. 47: a.icon-facebook
49. 48: a.icon-instagram
50. 49: a.icon-youtube
51. length: 50
52. __proto__: NodeList
</code></pre>
<p class="caption">Figure 65 - the node list returned by document.querySelectorAll("a")</p>
<p>Remember that the node list is stored in an array so we can use array notation to return a specific element. For example</p>
<pre><code>
document.querySelectorAll("a")[47]
</code></pre>
<p>will return</p>
<pre><code>
&lt;a class="icon-linkedin" data-grunticon-embed="" href="https://linkedin.com"&gt;&lt;span class="screen-reader-text"&gt;LinkedIn&lt;/span&gt;&lt;/a&gt;
</code></pre>
<p>That's the element in the node list with index value 45, as can be seen in figure 65.</p>
<p>If we want to access several elements of the array, we can assign it to a variable to make access slightly easier.</p>
<pre><code>
my_links=document.querySelectorAll("a")
</code></pre>
<p>We could then access the element with index value 45 with</p>
<pre><code>
my_links[45]
</code></pre>
<p>Since the array of links is an array of HTML objects, in effect, they do have properties. As an example, the 'Your Stay' link at the top of the page is element 3 in the array of links. We could access a property such as its' Inner HTML like this.</p>
<pre><code>
document.querySelectorAll("a")[3].innerHTML
</code></pre>
<p>This is probably not the best way to do it, particularly if you want to work with a lot of properties for a lot of links on the page. It would probably be better to start by creating a reference to the array of links with</p>
<pre><code>
my_links=document.querySelectorAll("a")
</code></pre>
<p>We can then create a reference to the link we are interested in with</p>
<pre><code>
staylink=my_links[3]
</code></pre>
<p>From there, it is really easy to access an individual property so we might access the innerHTML with</p>
<pre><code>
staylink.innerHTML
</code></pre>
<p>or we can access the URL of the link which is the href property with</p>
<pre><code>
staylink.href
</code></pre>
<p>In some cases, it can help to know something of the layout of the HTML. For example, the social media links at the bottom of the page all belong to the class, social-nav. I don't know if this is a convention or if it just happens to be the way this page is organised, but it allows us to return all social media links with</p>
<pre><code>
document.querySelectorAll(".social-nav a")
</code></pre>
<p>We can also use a similar technique to get a specific icon or subset of icons with something like</p>
<pre><code>
document.querySelectorAll(".social-nav a[href='linkedin.com']")
</code></pre>
<p>This should return any link pointing to linkedin.com inside an element with class social-nav.</p>
<p>We can also combine different selectors with a comma so</p>
<pre><code>
document.querySelectorAll(".menu li a, .social-nav li a")
</code></pre>
<p>will return all links that are inside link items inside either the menu (with class .menu) or the social media links (with class .social-nav).</p>
<p>The querySelector and querySelectorAll methods are quite powerful and because they utilise CSS selectors, they will be intuitive and easy-to-use for most web developers.</p>
<p>For more information, the online documentation for the document is pretty useful and can be found at <a href="https://developer.mozilla.org/en-US/docs/web/api/document">https://developer.mozilla.org/en-US/docs/web/api/document</a>.</p>
<h1 class="sectiontitle">Access and Change Elements</h1>
<p>There are a lot of properties that are common to all elements in the DOM. In the previous section, we saw how it was possible to use querySelector or querySelectorAll to target these element or return their properties. Many of these properties are read-only, as can be seen in the documentation at <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element"></a>.</p>
<p>Some of these properties provide meta information such as scroll top which holds the number of pixels between the top of the element and the top of the document.</p>
<p>There are also some elements that are very useful to be able to manipulate, and doing this is as easy as accessing a property with querySelector.</p>
<p>As an example, in the web site from the exercise files, there is an &lt;h2&gt; element with the text, "Welcome to Moonwalk Manor". We can access this element with</p>
<pre><code>
document.querySelector(".main-title")
</code></pre>
<p>If we want to access the text in the heading, we can get that by appending innerHTML to the above.</p>
<pre><code>
document.querySelector(".main-title").innerHTML
</code></pre>
<p>We can also get the whole tag, including any attrbiutes, with outerHTML.<p>
<pre><code>
document.querySelector(".main-title").outerHTML
<code></pre>
<p>If we want to change the text of this heading, we simply retrieve it and assign it a new value.</p>
<pre><code>
document.querySelector(".main-title").innerHTML="We are taking control of this element!"
</code></pre>
<p>The result is shown in figure 66.</p>
<img src="images/image36.png" alt="Figure 66 image">
<img src="images/image36.png" alt="Figure 66 image">
<p class="caption">Figure 66 - the website from the exercise files displaying the modified h2 element</p>
<p>It might be worth mentioning a very important lesson here. The result shown above represents my second attempt to amend the heading. The first time I tried it, I used this command.</p>
<pre><code>
document.body.innerHTML="We are taking control of this element!"
</code></pre>
<p>This was the result, as you might expect.</p>
<img src="../../webdevelopment/javascriptessentialtraining2017/images/image37.gif" alt="Figure 67 image">
<p class="caption">Figure 67 - my first unsuccessful attempt to amend the heading</p>
<p>I'm not quite sure how this mistake happened, but the result is that instead of replacing the text in the heading, the command actually replaces the body. Actually, that is not precisely true. As a matter of interest, changed by this command. This may surprise you but what is actually happening is that we are changing a property of the HTML element, not the HTML file. That is, the page is loaded, we change the text in the heading dynamically and it remains changed until we refresh or reload the page.</p>
<p>There is a little speculation here, but I guess that what is happening is that when the page is initially loaded, it creates the DOM. The heading is a node in the DOM with the attributes and text that are specified in the HTML. We use a JavaScript command to change the property in the DOM, but this is not written back to the original HTML file.</p>
<p>Actually, if you think about it, this makes perfect sense given that JavaScript was created as a client side scripting language and you wouldn't want the client to be able to amend the files on the server. So, when we reload the page, we get the original HTML file back and our changes are essentially flushed.</p>
<p>Bear in mind that this change was made through the console and JavaScript is written in the console as part of the learning process. In reality, this is not how you would make changes to the file. For instance, if you actually want the heading to display the text, "We are taking control of this element!", you would just write that into the HTML file on the server.</p>
<p>You would use JavaScript, typically, to amend the HTML in response to some event. In such a case, the JavaScript would be written into an appropriate script file which is loaded when the page is loaded so your JavaScript doesn't change when the page is reloaded. However, if you make dynamic changes to the page after loading it, these changes would probably disappear when the page was reloaded in that it would revert to its original state.</p>
<p>However, you should still remember that you can make a real mess of a web page simply by targeting the wrong element. It also emphasises the fact that it is important to test your code to avoid potentially embarrassing mistakes like that.</p>
<p>We can also modify existing properties. For instance, we have a div with the id, showcase. We can retrieve this using a querySelector, as before, and then set it to a new value.</p>
<pre><code>
document.querySelector("#showcase").id = "slideshow"
</code></pre>
<p>What we are doing here is using querySelector to get an element from within the document. This is the element with id showcase (the # in front of the name tells us that it is an id). We then access the id property of this element and use the assignment operator to set it to "slideshow".</p>
<p>In the element inspector, we can see that there is now a div with the id of slideshow. If we were to provide a brief life-history of this element, we might say that it was created inside the HTML file and given an id of "showcase". We then used a JavaScript command (from the console, but as I said before, this would normally come from either JavaScript embedded in the HTML file or in a separate script) to change the id to "slideshow".</p>
<p>This is important because you should remember that JavaScript set this value to something that is not in the HTML file. The HTML file is not changed by this so if you reload or refresh the page, the element will have an id of "showcase" again.</p>
<p>Just to reiterate, the only real point to remember here is that making changes in the console are generally temporary.</p>
<p>Elements can have two class names. For example, if we look at the className property of the masthead element we looked at earlier</p>
<pre><code>
document.querySelector(".masthead").className
</code></pre>
<p>this gives us the output</p>
<pre><code>
"masthead clear"
</code></pre>
<p>So, we can see the class name, masthead and a second class name, clear. We can also return these as a list with</p>
<pre><code>
document.querySelector(".masthead").classList
</code></pre>
<p>The output we get from this is</p>
<pre><code>
1. DOMTokenList(2) ["masthead", "clear", value: "masthead clear"]
1. 0: "masthead"
2. 1: "clear"
3. length: 2
4. value: "masthead clear"
5. __proto__: DOMTokenList
</code></pre>
<p>If we want to access an individual element, that is, the class name "masthead" or the class name "clear", since this is an array, we can use array notation.</p>
<pre><code>
document.querySelector(".masthead").classList[0]
</code></pre>
<p>or</p>
<pre><code>
document.querySelector(".masthead").classList[1]
</code></pre>
<p>and these commands will return "masthead" and "clear" respectively.</p>
<p>This implies that we can change the class name with a command such as</p>
<pre><code>
document.querySelector(".masthead").classList[1]="unclear"
</code></pre>
<p>The result from doing this is that we see that "unclear" is returned. If we try to get the second element in the class list again with</p>
<pre><code>
document.querySelector(".masthead").classList[1]
</code></pre>
<p>the value returned is "clear". If you go back to the documentation for elements and look up classList() - <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/classList">https://developer.mozilla.org/en-US/docs/Web/API/Element/classList</a> - you will see that it is a read-only property. That doesn't mean that it can't be changed, but like the private properties of an object, we have to use the methods created for this purpose. We will look at how to do that next.</p>
<h1 class="sectiontitle">Access and Change Classes</h1>
<p>The classList property had 5 methods associated with it. These are add, remove, item, toggle and contains and we use these methods to interact with classList.</p>
<p>Recall that we can return a classList, such as the one associated with masthead, with a command such as</p>
<pre><code>
document.querySelector(".masthead").classList
</code></pre>
<p>This returns the list containing two values</p>
<pre><code>
DOMTokenList(2) ["masthead", "clear", value: "masthead clear"]
</code></pre>
<p>If we want to add another value to the list, we do that by appending the add method to this statement. Let's say we want to add the class name, new-class, we do that with.</p>
<pre><code>
document.querySelector(".masthead").classList.add("new-class")
</code></pre>
<p>If we go and get the class list again, we will see that it now looks like this.</p>
<pre><code>
DOMTokenList(3) ["masthead", "clear", "new-class", value: "masthead clear new-class"]
</code></pre>
<p>Similarly, if we want to remove a class, we use the remove method, so if we want to remove "clear", we do it like this.</p>
<pre><code>
document.querySelector(".masthead").classList.remove("clear")
</code></pre>
<p>And the class list now looks like this.</p>
<pre><code>
DOMTokenList(2) ["masthead", "new-class", value: "masthead new-class"]
</code></pre>
<p>If you try to add a class name that is already in the list, this is ignored so essentially, the list is a set, or at least, displays some of the properties of a set.</p>
<p>As a matter of interest, it is possible to remove an item by referring to the index value but this may be a little complex. For example, assuming we don't have a reference to the list, we need to return the list so that we can pass a method to it, but we also need to return the list as an argument, so it will look something like this</p>
<pre><code>
document.querySelector(".masthead").classList.remove(document.querySelector(".masthead").classList[2])
</code></pre>
<p>A more practical way to do this would be to create a reference, let's say mastheadClasses with something like</p>
<pre><code>
var mastheadClasses = document.querySelector(".masthead").classList
</code></pre>
<p>We can then do essentially the same thing using the reference to fetch the list</p>
<pre><code>
mastheadClasses.remove(mastheadClasses[2])
</code></pre>
<p>Actually, this does show that the original syntax using the actual value is very clean and concise, so you would probably always use that whenever possible.<p>
<p>The item method looks like a bit of a strange one. Its function is to return the item with the specified index so, for example</p>
<pre><code>
document.querySelector(".masthead").classList.item(0)
<p>returns the first item.</p>
<p>This is exactly the same as</p>
<pre><code>
document.querySelector(".masthead").classList[0]
</code></pre>
<p>so it is hard to see what benefit we get from using the item method (if any).</p>
<p>The toggle method is used to switch a class on or off. To do this, you may need to target a different class, especially if you want to alternate between states rather than do this as a one-off. For instance, let's say we want to switch off the masthead class. We do this with</p>
<pre><code>
document.querySelector(".new-class").classList.toggle("masthead")
</code></pre>
<p>Assuming it was already on, this will switch it off and in the console, you will get a false value returned, since the masthead class, in effect, no longer exists. If we execute the same statement again, we will see true being returned.</p>
<p>Since switching a class off more or less means that it no longer exists, if we try to use its value as a selector like this, for instance</p>
<pre><code>
document.querySelector(".masthead").classList[0]
</code></pre>
<p>we will get an error if masthead is currently switched off. We can also get a class list and we will not see any class in that list if it is switched off. However, it does still exist because we can toggle it back on. However, if we do this</p>
<pre><code>
document.querySelector(".new-class").classList[0]
</code></pre>
<p>is switched off, we will get new-class returned, so masthead no longer occupies the first space in the list.</p>
<p>If we do this</p>
<pre><code>
document.querySelector(".masthead").classList.toggle("masthead")
</code></pre>
<p>this will work if masthead exists in the class list, but if we try to do it again, we will get an error since masthead no longer exists. This is why we really need to target another class in the list when we do this. If we didn't, we might try to execute the statement while masthead doesn’t exist and get an error.</p>
<p>We can guard against this type of error by checking whether the class we want to target exists before we try to execute a statement like this. We can do that with the contains method which looks like this.</p>
<pre><code>
document.querySelector(".new-class").classList.contains("masthead")
</code></pre>
<p>Bear in mind that this will give an error if new-class doesn't exist (or has been switched off).</p>
<p>There may be a number of cases where you want to use methods to manipulate the properties of an element in the DOM and it's unlikely that you will remember them all, so in most cases, you will want to reference the documentation to see what methods are available for this.</p>
<p>Almost all of the read-only properties of an element have methods you can use like this and they should all be listed in the documentation. As a reminder, the URL for this documentation is <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element">https://developer.mozilla.org/en-US/docs/Web/API/Element</a>.</p>
<h1 class="sectiontitle">Access and Change Attributes</h1>
<p>Like classes, attributes are a read-only property of an element and they are represented by key-value pairs such as the href attribute with an associated URL. We can access them via the attributes property, but we need methods to actually work with them. There are 4 methods we can use</p>
<ul>
<li>has Attribute - returns true or false depending on whether the specified attribute is present</li>
<li>getAttribute - returns the value of an attribute</li>
<li>setAttribute - takes two arguments, an attribute name and a value and will either add an attribute with that name and value if one doesn't already exist, or if an attribute with that name does exist, it changes the value to the specified value</li>
<li>removeAttribute - removes the attribute</li>
<ul>
<p>We can use these methods with any attribute of any element. To see how this works, we will look at the website in the exercise files and we want to ensure that the Book Now link opens up a new tab when you click it. To do this, it needs to have a target attribute with a value of _blank.</p>
<p>If we look at the element in the inspector, we can see that it is inside a &lt;div&gt; with a class of "cta" and the link has a href attribute with a value of "#".</p>
<p>We will open the script.js file which is in the JS folder and is currently empty. To make it easier to work with the element, we will create a constants that references it with</p>
<pre><code>
const CTAELEMENT = document.querySelector(".cta a");
</code></pre>
<p>If we wanted to see what attributes the element currently has, we can do that with</p>
<pre><code>
console.log( CTAELEMENT.attributes );
</code></pre>
<p>We could do this in the script but in most cases, you would probably want to do that to check an attribute name or something like that so you would probably run that in the console directly.</p>
<p>We don't really need to test for the existence of the attribute because the setAttribute will create the required attribute if necessary. However, in this case, we will use a conditional statement to check whether the attribute exists. If it does, we will output the value to the console, and if it doesn't, we will add it. So this will be.</p>
<pre><code>
1. if (CTAELEMENT.hasAttribute("target")) {
2. console.log( CTAELEMENT.getAttribute("target"));
3. } else {
4. CTAELEMENT.setAttribute("target", "_blank")
5. }
</code></pre>
<p>Again, we can console.log the attribute property of CTAELEMENT and we can see that where it previously had one named attribute, it now has two. These are href and target.</p>
<p>If we edit the html file for this exercise, we can find the cta element with the 'Book Now!' link and manually add the attribute.</p>
<p>Refreshing the page, we can see that the value of the target attribute is logged to the console because our conditional statement now returns a true value.</p>
<p>It was mentioned that setAttribute will set the attribute if it isn't already present, so we don't need to check whether it exists or not but I just wanted to double check that so as a final test, I have amended script.js so that it doesn't use a conditional statement but just goes ahead and sets the value of the attribute, target to '_blank'. Bear in mind the fact that the html file has been updated so that CTAELEMENT does have the attribute with the value '_blank' and as might be expected, this simply has no effect, but it doesn't give an error.</p>
<h1 class="sectiontitle">Add DOM Elements</h1>
<p>One way to add an element to a document is by using a combination of inner and outer html. As an example, to add a caption to a figure element would involve retrieving the iner HTML of the figure, adding our caption HTML to it and injecting it back into the figure. This can be error-prone and an error here could break the HTML.</p>
<p>A better way involves four steps.</p>
<ol>
<li>Create the element</li>
<li>Create the text node that goes inside the element</li>
<li>Add the text node to the element</li>
<li>Add the element to the DOM tree</li>
</ol>
<p>There are three methods we need to do this.</p>
<ul>
<li>createElement();</li>
<li>createTextNode();</li>
<li>appendChild(); - places one child node inside another</li>
</ul>
<p>Clicking on any of the method names above will take you to the relevant page in the documentation.</p>
<p>To demonstrate this, there is an HTML document in the exercise files called moonwalk.html and this features an image which we will add a caption to.</p>
<p>This image is inside a figure element as shown in figure 68. It has ab Alt attribute which shows some text describing the image, so what we want to do is to use that text to create our caption for the image.</p>
<pre><code>
1. &lt;figure class="featured-image"&gt;
2. &lt;img src="images/testimonials/bluepebble.jpg" alt="Earthrise: A photograph of the Earth and parts of the Moon's surface taken by astronaut William Anders in 1968, during the Apollo 8 mission."&gt;
3. &lt;/figure&gt;
</code></pre>
<p class =>Figure 68 - the figure element containing the image in moonwalk.html </p>
<p>We will start by creating a couple of constants which are going to reference both the figure and the image inside the figure. These will go into the script.js file in the JS folder.</p>
<pre><code>
1. const FEATURED = document.querySelector(".featured-image");
2. const THEIMAGE = FEATURED.querySelector("img");
</code></pre>
<p>Notice that the second constant uses the first constant in order to target a nested element. This is something you will see quite often in JavaScript as a method to burrow into a document and access nested elements like this.</p>
<p>The next step will be to get the alt attribute from the image and we will create a variable to hold this text.</p>
<pre><code>
var altText = THEIMAGE.getAttribute("alt");
</code></pre>
<p>Next, we we will create a figcaption element.</p>
<pre><code>
var captionElement = document.createElement("figcaption");
</code></pre>
<p>Again, we have also created a variable to hold the new element. Note that this element now exists in the browser's memory, so the browser can access it but as yet, it has not been added to the document.</p>
<p>Next, we are going to create a text node which will hold the text to be added to the figcaption element and a variable to hold it. We will populate this with altText, which holds the text from the original alt attribute.</p>
<pre><code>
var captionText = document.createTextNode(altText);
</code></pre>
<p>We can now add the text node to our figcaption.</p>
<p>Just to make sure everything is working to this point, we want to add a line that logs captionText to the console.</p>
<pre><code>
1. console.log(captionElement);
</code></pre>
<p>Assuming everything works as it should, we should see that captionElement output in the console like this.</p>
<pre><code>
1. &lt;figcaption&gt;
2. "Earthrise: A photograph of the Earth and parts of the Moon's surface taken by astronaut William Anders in 1968, during the Apollo 8 mission."
3. &lt;/figcaption&gt;
</code></pre>
<p>It might be worth noting here that the output looks a lot cleaner in Chrome that in Firefox which suggests, to me, that Chrome might be a better tool for development. I generally prefer Firefox but I like developing with Chrome so I may adopt it as my development browser although, of course, I would still use Firefox along with any other major browser for testing purposes.</p>
<p>Once we are happy that everything is working, we can get rid of the line that logs captionElement to the console. Now, we want to append our figcaption to the image. This now adds the caption to the page, but we don't want to have this text as both the caption and the alt text. Adding it a caption makes the alt text redundant so we will also want to set this to blank.</p>
<pre><code>
THEIMAGE.setAttribute("alt", "");
</code></pre>
<p>This is a better method that using inner and outer HTML. It's also more reliable but still a little clunky. There is a better method, but it isn't supported by older browsers, so it isn't as useful if you want to create something that is compatible with as many versions of browsers as possible.</p>
<p>The newer method is append and it will append any string to a specified apparent node. We can get rid of the line in our code that creates a text node and also the two lines that use the appendChild() method.</p>
<p>Instead, we will just add the text directly to our captionElement</p>
<pre><code>
captionElement.append(altText);
</code></pre>
<p>and then add the captionElement to our image element (by this, I mean the element that contains the image).</p>
<pre><code>
FEATURED.append(captionElement);
</code></pre>
<p>We can then refresh the browser and see that the result is exactly the same.</p>
<p>It is important to remember that if you do use the append method, there may be some additional work required to make sure your code works in older browsers. In particular, you may need to look at polyfills for older versions of IE. There is a lot of information available online but the following sites are worth mentioning.</p>
<p><a href=https://polyfill.io/v3/>https://polyfill.io/v3/</a> - it looks like this automatically returns a set of polyfills given a set of requirements and a specific browser, so I guess you could point your website to this one and when someone accesses the site from a browser that needs one or more polyfills, the polyfill site will be connected to and will return whatever polyfills are required.</p>
<p><a href=https://medium.com/hackernoon/polyfills-everything-you-ever-wanted-to-know-or-maybe-a-bit-less-7c8de164e423>https://medium.com/hackernoon/polyfills-everything-you-ever-wanted-to-know-or-maybe-a-bit-less-7c8de164e423</a> - seems to have a lot of information relevant to polyfills.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData/append"> https://developer.mozilla.org/en-US/docs/Web/API/FormData/append </a> - the documentation for the append method.</p>
<h1 class="sectiontitle">Apply Inline CSS to an Element</h1>
<p>JavaScript can also be used to add custom CSS to elements. It does this by accessing an element's style attribute and adding sone inline CSS. These are stored within the style property and can be accessed like any other property.</p>
<p>For instance, let's say that we want to check the styles for a particular element, we simply call that element and access the styles property.</p>
<pre><code>
document.querySelector(".cta a").style
</code></pre>
<p>This will return a list of styles like this.</p>
<pre><code>
CSSStyleDeclaration {alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}
</code></pre>
<p>This is a CSS style declaration list. Note that the list includes all possible properties you might apply using an inline style but in this case, no inline CSS has been applied to the element so all the values are empty.</p>
<p>It is also important that this list just gives you the inline styles for the element. If styles are being applied by an external style sheet or an inline CSS rule in the document head, you won't see them here.</p>
<p>We can add styles to the styles property in a couple of ways. To assign a specific style property, we can target it directly.</p>
<p>Before we do that, the element looks like this.</p>
<pre><code>
1. &lt;div class="cta"&gt;
2. &lt;a href="#"&gt;Book Now!&lt;/a&gt;
3. &lt;/div&gt;
</code></pre>
<p>We would target the element and set the style property like this.</p>
<pre><code>
document.querySelector(".cta a").style.color="green";
</code></pre>
<p>As expected, this will change the font colour for the element to green.</p>
<p>This will work for any CSS property but if the property name in CSS is hyphenated, in JavaScript, this is converted to camel case. For example, let's say we want to change the elements background colour. In CSS, this is represented by background-color.</p>
<p>So, if we want to use the styles property to changer the background colour, we would do it like this.</p>
<pre><code>
document.querySelector(".cta a").style.backgroundColor="blue";
</code></pre>
<p>If we look at the element in the DOM (that is, in the elements tab in the Developer tools), it now looks like this</p>
<pre><code>
1. &lt;div class="cta"&gt;
2. &lt;a href="#" style="color: green; background-color: blue;"&gt;Book Now!&lt;/a&gt;
3. &lt;/div&gt;
</code></pre>
<p>One thing to note here is that when we apply a style in this way, such as when we change the background colour, this doesn't affect any other styles we may have applied (the font colour is still green). The problem is that we have to target each style property individually in separate statements.</p>
<p>We can also group our style properties in a single string which allows us to modify several style properties at the same time but also completely overwrites any style properties that are there already. To do this, we make use of the CSS styles property like this.</p>
<pre><code>
document.querySelector(".cta a").style.cssText = "padding: 1em; colour: white; background-color: red;"
</code></pre>
<p>We did set the background-color here (which had previously changed to blue) but if we didn't the change we made earlier would disappear when we apply styles in this way. Notice, also, that the syntax here is exactly the same as we would use when typing CSS into, for instance, an external style sheet so we are using CSS syntax here so, where necessary, names are hyphenated rather than camel case.</p>
<p>This type of approach is treating the styles as a property of an element, but we can also think of this as an attribute of the element (attributes map to properties).</p>
<p>Earlier, we saw that there were methods for manipulating attributes. As a reminder, these are</p>
<ul>
<li>has Attribute - returns true or false depending on whether the specified attribute is present</li>
<li>getAttribute - returns the value of an attribute</li>
<li>setAttribute - takes two arguments, an attribute name and a value and will either add an attribute with that name and value if one doesn't already exist, or if an attribute with that name does exist, it changes the value to the specified value</li>
<li>removeAttribute - removes the attribute</li>
</ul>
<p>So, we can use the setAttribute method. The attribute name is "style" and the value is the same string of CSS we saw in the last example.</p>
<pre><code>
document.querySelector(".cta a").setAttribute("style", "padding: 2em; color: green; background-color: orange;")
</code></pre>
<p>As in the previous example, this will remove any previously styles applied via one of these inline methods.</p>
<p>It is also worth noting that inline CSS is opinionated. That is, it will override any CSS that may have been applied to the element previously. I think this is sort of obvious. For instance, with out CTA element, the font colour is white and this could be a default value or it could be set in an external CSS file. However, if we use inline CSS to change this to green, this will override the white colour, regardless of where it had previously been set.</p>
<p>It is generally considered to be good practice when you are applying basic style changes via JavaScript, to target a class selector and then either add these classes to the element to apply the styles or remove the class to remove the styles. I'm not completely sure how you would do this but hopefully we will see this in action as the course progresses.</p>
<p>This method of adding CSS styles inline is clean and easy to manage and it preserves the basic separation of responsibilities whereby styling is controlled by CSS (with HTML being responsible for content and JavaScript being responsible for interactivity).</p>
<p>Actually, I think that this is an important point because it seems to me that styling is probably best done in an external CSS file with this type of inline CSS being used when there is some reason for applying some styling after the page has been loaded. The fact that it is done in JavaScript implies that it is tied in with the page's interactivity but this could also cover things like animation, a picture gallery, really anything that needs to be changed dynamically.</p>
</article>
<button class="previous" onclick="window.location.href='functionsandobjects.html';">
Previous Chapter - Functions and Objects
</button>
<button class="next" onclick="window.location.href='clock.html';">
Next Chapter - Project: Create an Analog Clock
</button>
<button class="coursebutton" onclick="window.location.href='javascriptessentialtraining.html'">
Course Contents
</button>
<button class="webdevbutton" onclick="window.location.href='/webdevelopment/webdevelopment.html'">
Web Development Page
</button>
<button class="homebutton" onclick="window.location.href='/index.html'">
Home
</button>
</body>
3 months ago
</html>