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.
 
 
 
 

278 lines
22 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 2: Events</h1>
<h1 class="sectiontitle">What Are DOM Events</h1>
<p>Everything that happens in the browser is an event, including the following</p>
<ul>
<li>open browser</li>
<li>enter a URL in the address bar</li>
<li>move the mouse</li>
</ul>
<p>More information on events in general can be found <a href="https://developer.mozilla.org/en-US/docs/Web/Events">here</a>.</p>
<p>The JavaScript we have seen so far is passive. That is, it runs when the page is loaded or we interact with the browser, for instance by refreshing the page. Event handlers are different in that they execute in response to specific events rather than just being executed when the page loads.</p>
<p>There are three steps in creating an event handler.</p>
<ol>
<li>Step 1 - identify the DOM node associated with the event</li>
<li>Step 2 - identify the event that should trigger the event handler</li>
<li>Step 3 - write a function to be triggered by the event handler</li>
</ol>
<h1 class="sectiontitle">Some Typical DOM Events</h1>
<p>Just as the browser and the document are both object giving us both a BOM and a DOM, we also have browser and document events and again, the link to the documentation for events which provides a full list of these is <a href="https://developer.mozilla.org/en-US/docs/Web/Events">here</a>.</p>
<p>Browser-level events relate to browser behaviour, so this covers things like loading a page, resizing a window and so on. Document-level events are related to interaction with the page content so this covers things like clicking on something inside the page (such as a link or a download button), changing focus or submit which such as when a form is submitted.</p>
<p>Figures 82 and 83 show some of the BOM and DOM events, respectively, that we would be likely to be using most often.</p>
<img src="images/image42.png" alt="Figure 82 image">
<p class="caption">Figure 82 - common browser-level events</p>
<img src="images/image43.png" alt="Figure 83 image">
<p class="caption">Figure 83 - common document-level events (often referred to as DOM events)</p>
<p>For obvious reasons, you will be working more with DOM events that browser-level events. In addition, there are events that are not really either BOM or DOM events and some of the more common of these are shown in figure 84.</p>
<img src="images/image44.png" alt="Figure 84 image">
<p class="caption">Figure 84 - other events</p>
<p>The number of events available makes it difficult, if not impossible to remember them all so you will probably need to use the online events reference when writing event handlers. As a rule, you should remember that any event that the browser reacts to is an event you can write an event-handler for.</p>
<h1 class="sectiontitle">Trigger Functions with Event Handlers</h1>
<p>For this exercise, another class has been added to the HTML file. This is called hide and it has been added to the div with class CTA. The styles applied to the class, hide, are shown in figure 85.</p>
<pre><code>
.hide {
height: 0;
overflow: hidden;
}
</code></pre>
<p class="caption">Figure 85 - the CSS styles applied to the class hide</p>
<p>We have also added an alert which displays some text when it is not possible to make a booking as shown in figure 86.</p>
<pre><code>
&lt;section id="booking-alert" class="booking-info"&gt;
&lt;div class="centered"&gt;
&lt;h3 class="content-title"&gt;Sorry about that!&lt;/h3&gt;
&lt;p class="teaser two-column"&gt;By some weird coincidence, all the rooms at Moonwalk Manor are booked out for the date you planned to take your trip. Unfortunately we are unable to provide you with sufficient lodging for your party at this time. Moonwalk Manor is very busy and your satisfaction is our top priority. When you take your first steps in the moon dust, your life will change. We are always at your service. Your satisfaction matters to us.&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;&lt;!-- .booking-info --&gt;
</code></pre>
<p class="caption">Figure 86 - the booking alert</p>
<p>The result of this is that when the page is first loaded, the 'Book Now' button is hidden (that is, we have set the height to 0 and overflow to hidden, the latter prevents the text in the button from being visible.</p>
<p>You may recall that we can use the querySelector classList methods to add a class to an element or remove it. In this example, we want either the 'Book Now' button to be visible or the alert to be visible and since it is the CSS applied to the hide class that causes the 'Book Now' button to be hidden, we can achieve this by adding or removing the class from these two elements as required.</p>
<p>In our JavaScript file, we will create a couple of constants so that we can easily access both these elements.</p>
<pre><code>
const CTA = document.querySelector(".cta");
const ALERT = document.querySelector("#booking-alert");
</code></pre>
<p>Next, we will use these constants to switch the hide class off for CTA and on for ALERT.</p>
<pre><code>
CTA.classList.remove("hide");
ALERT.classList.add("hide");
</code></pre>
<p>When we refresh the page, we can see that the alert is now hidden and the 'Book Now' button is once again visible.</p>
<p>This isn't really interactive in that the button and the alert are both either hidden or displayed when the page opens (or refreshes). We want to be able to change which is hidden and which is displayed in response to an event. Specifically, we want the 'Book Now' button to be hidden when we click on it and at the same time, we want to display the alert. Conversely, we also want to create an event to hide the alert and display the 'Book Now' button.</p>
<p>It is worth noting that since we have used HTML to hide the 'Book Now' button and display the alert and then used the JavaScript file to reverse these, if the page is loaded in a browser without JavaScript enabled, they will see the alert rather than the 'Book Now' button. If JavaScript is enabled, the user will see the 'Book Now' button and not the alert. So we can use this type of technique to control what the user sees if they don't have JavaScript enabled.</p>
<p>For example, you may have a div containing some text saying that JavaScript is not enabled and set it to display in the HTML file and then use the JavaScript file to hide it. As a result, the message is not hidden if the JavaScript file is not executed.</p>
<p>The next thing we want to do is to create a function which we will call reveal and this will toggle the hide class on both CTA and ALERT.</p>
<pre><code>
function reveal() {
CTA.classList.toggle("hide");
ALERT.classList.toggle("hide");
}
</code></pre>
<p class="caption">Figure 87 - the reveal function</p>
<p>The next part is to ensure that the reveal function is executed when the button is clicked and we do that with an event handler. The event, in this case, is onclick and we bind it to our CTA element like this.</p>
<pre><code>
CTA.onclick = reveal;
</code></pre>
<p>Now, if we reload the page, we will initially see the 'Book Now' button, but when we click on it, it disappears and the alert becomes visible.</p>
<p>You might notice that whenever the button is clicked, the page seems to refresh and you are taken to the top of the page. This is because of the href attribute</p>
<pre><code>
&lt;a href="#" class="hide"&gt;Book Now!&lt;/a&gt;
</code></pre>
<p>When this happens, you will see that the URL in the browser's address bar has a# symbol at the end, like this</p>
<pre><code>
file:///H:/My%20Learning/Code/JavaScript/Essential%20Training/CH07/07_03/index.html#
</code></pre>
<p>When the button is clicked, the link is triggered and this takes you to the top of the current page. We can prevent this from happening by hijacking the button's behaviour.</p>
<p>Remember that we are using an event handler here, and an event handler (obviously) is triggered by some event which happens to be a button-click here. The event itself is an object and we can treat it as an attribute in the function. By convention, the event object is referred to as e.</p>
<p>We will pass the event to the function and that allows us to reference it inside the function. We can then pass it the preventDefault() function which, as the name suggests, prevents the default behaviour so we no longer jump to the top of the page when the button is clicked.</p>
<h1 class="sectiontitle">Add and Use Event Listeners</h1>
<p>In some circumstances, using an event handler to deal with some event can work pretty well. That is, where the event triggers a single function based on some DOM-level event. In our previous example, we saw that with the line<p>
<pre><code>
CTA.onclick = reveal;
</code></pre>
<p>This had the effect of triggering the reveal function when the element represented by the CTA constant was clicked. Now, consider what would happen if we add this line to our JavaScript code.</p>
<pre><code>
CTA.onclick = console.log("The button was clicked!");
</code></pre>
<p>When we click the button, the message is logged to the console but the reveal function is not triggered. This is actually quite reasonable if you think about it. First, we have set the value of CTA.onclick to reveal (which causes the reveal function to be triggered). We have then set the value of CTA.onclick to the console.log statement so this has overwritten the first assignment.</p>
<p>In addition, you might notice that the event is only triggered once and we can also see that it is, again, going back to the top of the page when we click the button so we are no longer ignoring the default behaviour. This is because the event (e) was passed to the reveal function in order to use the preventDefault method with the event, but we are no longer calling that function.</p>
<p>If we want to associate more than one action with the button click event, we will need a different approach. Instead of using an event handler, we will use event listeners. An event listener will listen for an event and then pass that event over to a function.</p>
<p>To add an event listener to an element such as the button, we pass it the addEventListener method and this method takes three arguments.</p>
<ul>
<li>The event we want to listen for (in this case "click")</li>
<li>The function we want to bind this event listener to (in this case reveal) </li>
<li>The third argument can take a value of true or false. For the purpose of this course, we will always use the value, false. This defines how the browser captures the event. If the value is false, it captures the event from the most specific node (our button). If the value is true, we use the least specific node (the document). </li>
</ul>
<p>We would most likely use true as the third argument if we were creating a hierarchy of events and more information on this can be found on the w3.org site <a href="https://www.w3.org/TR/DOM-Level-3-Events/#event-flow">here</a>.</p>
<p>Putting these together gives us</p>
<pre><code>
CTA.addEventListener("click", reveal, false);
</code></pre>
<p>This replaces the event handlers. If we save this and reload the page, we see that it works as it did before with just a single event handler triggering the reveal function.</p>
<p>To add a second event listener, this is slightly more complicated since we just want to execute a console log statement, but the event listener expects a function as the second argument. We will solve this problem by using an anonymous function.</p>
<pre><code>
CTA.addEventListener("click", function () { console.log("The button was clicked!"); }, false);
</code></pre>
<p>Again, we will save this and refresh the page and we can see that as well as the reveal function being triggered when we click the button, we are also logging the message to the console log every time the button is clicked so both event handlers are working.</p>
<p>As a further demonstration of event listeners, we have an HTML file called mouse-tracker.html and if we load it, we see that it is basically an empty page except for a red circle. If we move the mouse, the circle moves in response. If we imagine that there are a pair of axis, centred on the screen, the circle is positioned as a reflection on both axis of the mouse pointer. That is, as we move the pointer up, the circle moves down and vice versa and as we move the pointer left, the circle moves right and vice versa.</p>
<p>This is all handler with event listeners in the file called tracker-script.js shown in figure 88.</p>
<pre><code>
const AREA = document.body;
const CIRCLE = document.querySelector('.circle');
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
function mouseCoordinates(e) {
// Capture the event object (mouse movement).
// Get X coordinate (distance from left viewport edge) via clientX property.
// Take total window width, subtract current coordinate and width of circle.
// Do the same for Y coordinate (distance from top viewport edge).
var horizontalPosition = windowWidth - e.clientX - 26;
var verticalPosition= windowHeight - e.clientY - 26;
// Set horizontal and vertical position.
CIRCLE.style.left = horizontalPosition + 'px';
CIRCLE.style.top = verticalPosition + 'px';
}
function changeColorOnTouch() {
CIRCLE.style.backgroundColor = "green";
CIRCLE.style.borderColor = "green";
}
// When the mouse moves, run the mouseCoordinates function.
AREA.addEventListener('mousemove', mouseCoordinates, false);
// When the mouse touches the circle, run the changeColorOnTouch function.
CIRCLE.addEventListener('mouseenter', changeColorOnTouch, false);
// When the mouse leaves the circle, remove inline styles with an anonymous function.
CIRCLE.addEventListener('mouseleave', function(){CIRCLE.removeAttribute("style");}, false);
</code></pre>
<p class="caption">Figure 88 - the tracker-script.js file</p>
<p>Note that when the mouse pointer is over the circle, it becomes a green filled-in circle so there are really three events we are interested in.</p>
<p>The first is the mouse movement which triggers the movement of the circle. The second is the transition from red circle to green filled-in circle when the pointer is over the circle. The second is the transition back to red circle when the pointer is no longer over the circle.</p>
<p>In the script (in figure 88) we start with two constants. The first one</p>
<pre><code>
const AREA = document.body;
</code></pre>
<p>references the body of the page. The second</p>
<pre><code>
const CIRCLE = document.querySelector('.circle');
</code></pre>
<p>references the circle.</p>
<p>We also have two variables which referencing the page dimensions.</p>
<pre><code>
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
</code></pre>
<p>At the end of the file, we have our three event listeners.</p>
<pre><code>
// When the mouse moves, run the mouseCoordinates function.
AREA.addEventListener('mousemove', mouseCoordinates, false);
</code></pre>
<p>The first (shown above) listens for the mouse moving and when it does, it calls the mouseCoordinates function which grabs the event and determines the x and y coordinates of the mouse pointer. It uses these to calculate the coordinates of the circle and uses these coordinates to set the top and left properties of the circle.</p>
<pre><code>
// When the mouse touches the circle, run the changeColorOnTouch function.
CIRCLE.addEventListener('mouseenter', changeColorOnTouch, false);
</code></pre>
<p>The second (shown above) listens for the mouse pointer entering the circle and calls the changeColorOnTouch function which changes the circle's background and border colours.</p>
<pre><code>
// When the mouse leaves the circle, remove inline styles with an anonymous function.
CIRCLE.addEventListener('mouseleave', function(){CIRCLE.removeAttribute("style");}, false);
</code></pre>
<p>The third listens for the mouse pointer leaving the circle and calls an anonymous function that removes the styles from the circle (changing it back to a red circle with no background colour.</p>
<h1 class="sectiontitle">Pass Arguments via Event Listeners</h1>
<p>One downside of the event listeners shown above is that the function that we bind the listener to does not have any parentheses and this means that is not possible to pass any arguments to that function. At least, not in this way.</p>
<pre><code>
CIRCLE.addEventListener('mouseenter', changeColorOnTouch, false);
</code></pre>
<p>Of course, it can be useful to be able to pass an argument to an event listener and it is possible to do this by changing the way we make the function call. As a side note, the reason we don't use parentheses here is because if we did, the function would be called as soon as the page loaded and that can lead to unexpected results.</p>
<p>Our other event handler used an anonymous function and this time, we did use parentheses</p>
<pre><code>
CIRCLE.addEventListener('mouseleave', function(){CIRCLE.removeAttribute("style");}, false);
</code></pre>
<p>which suggests that we might use the same method to pass arguments to the function handling the event.</p>
<p>To demonstrate this, we want to amend our code so that rather than hide the 'Book Now' button, we will change the text so that it says Oops! when the alert appears.</p>
<p>To begin with this, we will assume that an argument, which we will call current is being passed over to the function.</p>
<pre><code>
function reveal(current) {
</code></pre>
<p>We then want to change the call to this function so that we are calling it with an anonymous function which will allow us to pass over an argument. The argument we will pass is the element that we are calling the function from (that is, the CTA element which is the one we want to manipulate) so the argument will be this.</p>
<pre><code>
CTA.addEventListener('click', function () { reveal(this)}, false);
</code></pre>
<p>Inside the function, rather than use toggle to switch the class off, we will use the element we have passed over to the reveal function to amend it's innerHTML. That is, we will amend the text displayed inside the CTA element.</p>
<pre><code>
current.innerHTML == "Book Now!" ? CTA.innerHTML = "Ooooops!" : CTA.innerHTML = "Book Now!";
</code></pre>
<p>Notice that we passed the parameter over as this (so this is the actual parameter since it references the element we will manipulate in the function). The formal parameter is referenced with current so inside the function, current references the element we passed over.</p>
<p>Now, we can save this and reference the page, but we will find that it doesn't work. When we click the 'Book Now!' button, nothing happens. This is because we previously passed e (the event) over to the reveal function. We are no longer passing it over, but it is still referenced inside the function so we get this error.</p>
<pre><code>
script.js:8 Uncaught ReferenceError: e is not defined
at reveal (script.js:8)
at HTMLAnchorElement.&lt;anonymous&gt; (script.js:15)
</code></pre>
<p>We can easily fix this by commenting out the line</p>
<pre><code>
e.preventDefault();
</code></pre>
<p>The result is that we now see the behaviour we expected. When we click on 'Book Now!', the text in the button changes to 'Ooooops!' and the alert appears. If we click it again, the text reverts to 'Book Now!' and the alert disappears.</p>
<p>Of course, the problem is that we are seeing the default behaviour again where the page skips back to the top when the button is clicked.</p>
<p>A better solution is to pass the event over to the function along with this. We can't do this directly, that is</p>
<pre><code>
CTA.addEventListener('click', function () {reveal(e, this)}, false);
</code></pre>
<p>won't work because the anonymous function that is calling the reveal function is not able to directly the reference the event. Actually, if we try to do this, when we click the button, we get this error</p>
<pre><code>
script.js:15 Uncaught ReferenceError: e is not defined
at HTMLAnchorElement.&lt;anonymous&gt; (script.js:15)
</code></pre>
<p>The event listener can access the event, so we can get around this problem by passing the event from the event handler to the anonymous function. We can then pass it to the reveal function.</p>
<pre><code>
CTA.addEventListener('click', function (e) {reveal(e, this)}, false);
</code></pre>
</article>
<button class="previous" onclick="window.location.href='clock.html';">
Previous Chapter - Project: Create an Analog Clock
</button>
<button class="next" onclick="window.location.href='typingspeedtester.html';">
Next Chapter - Project: Typing Speed Tester
</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>
</html>