Introduction to JavaScript Events

1. What is an event?

Events are things that happen in the system you're programming - the system generates (or "triggers") some kind of signal when an event occurs and provides a mechanism to automatically take some action when the event occurs (i.e. run some code). Events are fired within the browser window and tend to be attached to the specific item residing within it. This could be a single element, a group of elements, the HTML document loaded in the current tab, or the entire browser window. There are many different types of events that can occur.

For example:

  • A user selects, clicks, or hovers over an element.
  • The user presses a key on the keyboard.
  • The user resizes the browser window or closes the browser window.
  • The web page finishes loading.
  • The form is submitted.
  • The video plays, pauses, or ends.
  • An error occurred.
  • As you can see from here (and from the MDN event reference docs), there are quite a few events that can be fired.

In order to react to an event, you attach an event handler to it. This is a block of code (usually a JavaScript function you create as a programmer) that runs when an event occurs. When such a block of code is defined to run in response to an event, we say we are registering an event handler. Note that event handlers are sometimes called event listeners - for our purposes the two names are the same, although strictly speaking this piece of code both listens to and handles events. Listeners watch for events to occur, and handlers respond when events occur.

Note: web events are not core to the JavaScript language - they are defined as an API built into the browser.

Example: Handling a click event

In this example we have a button element in the page:

<button>改变颜色</button>

Then we have some javascript. We'll discuss this in more detail in the next section, but for now let's say it: it adds an event handler to the button's "click" event, which reacts to that event by setting the page background to a random color :

const btn = document.querySelector("button");

function random(number) {
    
    
  return Math.floor(Math.random() * (number + 1));
}

btn.addEventListener("click", () => {
    
    
  const rndCol = `rgb(${
      
      random(255)}, ${
      
      random(255)}, ${
      
      random(255)})`;
  document.body.style.backgroundColor = rndCol;
});

2. Use addEventListener()

As we saw in the previous example, objects capable of firing events have an addEventListener() method, and this is the recommended mechanism for adding event handlers.

Let's take a closer look at the code for the previous example:

const btn = document.querySelector("button");

function random(number) {
    
    
  return Math.floor(Math.random() * (number + 1));
}

btn.addEventListener("click", () => {
    
    
  const rndCol = `rgb(${
      
      random(255)}, ${
      
      random(255)}, ${
      
      random(255)})`;
  document.body.style.backgroundColor = rndCol;
});

The HTML button element will fire an event when the user clicks the button. So it defines a addEventListener() function, which we call here. We want to pass in two parameters:

The string "click", indicating that we want to listen for click events. Buttons can trigger many other events, such as when the user moves the mouse over the button ("mouseover" event), or when the user presses a key and the button is focused ("keydown" event).
The function to call when the event occurs. In our case, the function generates a random RGB color and sets the background-color of the page body to that color.
It is also possible to have the handler function as a single named function, like this:

const btn = document.querySelector("button");

function random(number) {
    
    
  return Math.floor(Math.random() * (number + 1));
}

function changeBackground() {
    
    
  const rndCol = `rgb(${
      
      random(255)}, ${
      
      random(255)}, ${
      
      random(255)})`;
  document.body.style.backgroundColor = rndCol;
}

btn.addEventListener("click", changeBackground);

Listen for other events

There are many different events that can be triggered by a button element. Let's do an experiment.

First, make a copy of random-color-addeventlistener.html locally and open it in your browser. This is just a copy of the simple random color example we've already played with. Now try to change click to the following different values ​​one by one, and observe the results in the example:

focus and blur: the color changes when the button is focused or out of focus; try pressing the tab key to focus on the button, pressing it again to make the button unfocused. These events are typically used to display information filled in a form field when focused, or to display an error message when a form field is filled with an incorrect value.
dblclick: The color only changes when the button is double-clicked.
mouseover and mouseout: The color changes when the mouse pointer hovers over the button, or when the pointer moves out of the button, respectively.
Some events, such as click (click event), are available for almost any element. Other events are more specific and only useful in certain situations: for example, the play event only works on certain elements, such as the video element.

remove listener

If you added an event handler using addEventListener(), you can remove it again using the removeEventListener() method. For example, this will remove the changeBackground() event handler:

btn.removeEventListener("click", changeBackground);

Event handlers can also be removed by passing an AbortSignal to addEventListener() and then calling abort() on the controller that owns the AbortSignal. For example, to add an event handler that can be removed using AbortSignal, do this:

const controller = new AbortController();

btn.addEventListener("click",
  () => {
    
    
    const rndCol = `rgb(${
      
      random(255)}, ${
      
      random(255)}, ${
      
      random(255)})`;
    document.body.style.backgroundColor = rndCol;
  },
  {
    
     signal: controller.signal } // 向该处理器传递 AbortSignal
);

The event handler created by the above code can then be removed like this:

controller.abort(); // 移除任何/所有与该控制器相关的事件处理器

For simple small programs, cleaning up old, unused event handlers is unnecessary, but for larger, more complex programs, it can improve efficiency. Additionally, the ability to remove event handlers allows you to make the same button perform different actions in different situations: all you have to do is add or remove handlers.

Add multiple listeners on a single event

You can set multiple handlers for an event by making multiple calls to addEventListener(), each providing a different handler:

myElement.addEventListener("click", functionA);
myElement.addEventListener("click", functionB);

All handler functions run when the button is clicked.

learn more

There are many more powerful features and options for addEventListener().

These are somewhat beyond the scope of this article, see the addEventListener and removeEventListener reference pages if you want to learn more.

3. Other event listener mechanisms

We recommend that you use addEventListener() to register event handlers. This is the most powerful method, and it scales best in more complex programs. However, there are two other ways of registering event handlers that you may have seen: event handler attributes and inline event handlers.

Event Handler Properties Objects
that can trigger events (such as buttons) usually also have properties whose name is on, followed by the name of the event. For example, an element has an attribute onclick. This is called an event handler attribute. In order to listen for events, you can assign handler functions to this property.

For example, we could rewrite the random color example like this:

const btn = document.querySelector("button");

function random(number) {
    
    
  return Math.floor(Math.random() * (number + 1));
}

btn.onclick = () => {
    
    
  const rndCol = `rgb(${
      
      random(255)}, ${
      
      random(255)}, ${
      
      random(255)})`;
  document.body.style.backgroundColor = rndCol;
};

You can also assign handler attributes to named functions:

const btn = document.querySelector("button");

function random(number) {
    
    
  return Math.floor(Math.random() * (number + 1));
}

function bgChange() {
    
    
  const rndCol = `rgb(${
      
      random(255)}, ${
      
      random(255)}, ${
      
      random(255)})`;
  document.body.style.backgroundColor = rndCol;
}

btn.onclick = bgChange;

For event handler properties, you cannot add more than one handler for an event. For example, you can call addEventListener('click', handler) multiple times on an element, specifying different functions in the second argument:

element.addEventListener("click", function1);
element.addEventListener("click", function2);

This is not possible with event handler properties, since any subsequent attempts will overwrite earlier set properties:

element.onclick = function1;
element.onclick = function2;

Inline event handlers - don't use

You may also see this form in code:

<button onclick="bgChange()">按下我</button>
function bgChange() {
    
    
  const rndCol = `rgb(${
      
      random(255)}, ${
      
      random(255)}, ${
      
      random(255)})`;
  document.body.style.backgroundColor = rndCol;
}

The earliest methods of registering event handlers found on the web involved event handler HTML attributes (or inline event handlers), as shown in the example. The property value is the JavaScript code that you want to run when the event occurs. The example above calls a function defined inside a script element on the same page, but you can also insert JavaScript directly inside the attribute, for example:

<button onclick="alert('你好,这是来自旧式事件处理器的一条消息');">
  按下我
</button>

You can find HTML attribute equivalents for many event handler attributes; however, you should not use these attributes—they are considered bad practice. Using event handler properties might seem easy if you're doing something really fast, but they can quickly become unmanageable and inefficient.

First of all, it's not a good idea to mix your HTML with your JavaScript, because it becomes difficult to read. It's a good practice to separate your JavaScript, if it's in a separate file, you can apply it to multiple HTML documents.

Even in a single file, it's not a good idea to inline event handlers. One button is fine, but what if you have 100 buttons? You'd have to add 100 attributes to the file; this would quickly turn into a maintenance nightmare. Using JavaScript, you can easily add an event handler to all the buttons on the page, no matter how many there are, using something like this:

const buttons = document.querySelectorAll("button");

for (const button of buttons) {
    
    
  button.addEventListener("click", bgChange);
}

Finally, many common server configurations will disallow inline JavaScript as a security measure.

You should never use HTML event handler attributes - those are obsolete and it is bad practice to use them.

4. Event object

Sometimes inside an event handler, you may see a parameter with a fixed name, such as event, evt, or e. This is called an event object, and it is automatically passed to event handler functions to provide additional functionality and information. For example, let's rewrite our random color example a bit:

const btn = document.querySelector("button");

function random(number) {
    
    
  return Math.floor(Math.random() * (number + 1));
}

function bgChange(e) {
    
    
  const rndCol = `rgb(${
      
      random(255)}, ${
      
      random(255)}, ${
      
      random(255)})`;
  e.target.style.backgroundColor = rndCol;
  console.log(e);
}

btn.addEventListener("click", bgChange);

Here, you can see that we include an event object e in the function, and in the function set the background color style on e.target - which refers to the button itself. The target property of the event object e is always a reference to the element on which the event just occurred. So in this example we set a random background color on the button, not the page.

Note: See event delegation for an example of using event.target.

Note: You can use any name you like for the event object - just choose a name and you can refer to it in the event handler. Developers use e/evt/event most often because they are simple and easy to remember. It's always good to be consistent -- at least to yourself. Do the same with others, if possible.

Extra properties of the event object

Most event objects have a standard set of properties and methods, see the Event object reference for a complete list.

Some event objects add extra properties related to that particular type of event. For example, the keydown event occurs when the user presses a key. Its event object is KeyboardEvent, which is a specialized Event object with a key property that tells you which key was pressed:

<input id="textBox" type="text" />
<div id="output"></div>
const textBox = document.querySelector("#textBox");
const output = document.querySelector("#output");
textBox.addEventListener("keydown", (event) => {
    
    
  output.textContent = `You pressed "${
      
      event.key}".`;
});

5. Prevent default behavior

Sometimes, you come across situations where you want an event not to perform its default behavior. The most common example is a web form, such as a custom registration form. When you fill in the details and press the submit button, the natural behavior is to submit the data to a designated page on the server for processing, and redirect the browser to some kind of "success message" page (or the same page if the other doesn't specified).

The trouble starts when users don't submit data correctly - as a developer, you want to stop submitting information to the server and give them an error message telling them what went wrong and what needs to be done to fix it. Some browsers support automatic form data validation features, but since many do not, it is recommended that you do not rely on these features and implement your own validation checks. Let's look at a simple example.

First, here's a simple HTML form that requires you to fill in your first name and last name:

<form>
  <div>
    <label for="fname">First name: </label>
    <input id="fname" type="text" />
  </div>
  <div>
    <label for="lname">Last name: </label>
    <input id="lname" type="text" />
  </div>
  <div>
    <input id="submit" type="submit" />
  </div>
</form>
<p></p>

Next comes the JavaScript code - here we implement a very simple check in the handler of the submit event (which fires when the form is submitted) to test whether the text field is empty. If so, we call the preventDefault() function on the event object, stop the form submission, and then display an error message in the paragraph below our form to tell the user what went wrong:


const form = document.querySelector("form");
const fname = document.getElementById("fname");
const lname = document.getElementById("lname");
const para = document.querySelector("p");

form.addEventListener("submit", (e) => {
    
    
  if (fname.value === "" || lname.value === "") {
    
    
    e.preventDefault();
    para.textContent = "You need to fill in both names!";
  }
});

Obviously, this is a very weak form validation - for example, if the user enters a space or a number to submit the form, the form validation doesn't prevent the user from submitting - but it's good enough for a demonstration. The output is as follows:

6. Event bubbling

Event bubbling describes how browsers handle events for nested elements.

Set the listener on the parent element

Consider a web page like this:

<div id="container">
  <button>点我!</button>
</div>
<pre id="output"></pre>

Here is a button inside some other element div, let's say this div element here is the parent of the element it contains. What happens when we attach a click event handler to the parent element and click the button?

const output = document.querySelector("#output");
function handleClick(e) {
    
    
  output.textContent += `你在 ${
      
      e.currentTarget.tagName} 元素上进行了点击\n`;
}

const container = document.querySelector("#container");
container.addEventListener("click", handleClick);

You'll notice that when the user clicks the button, the click event fires on the parent element:

You clicked on the DIV element

This makes sense: the button is in the

inside, so when you click the button, you also implicitly click the element it's on.

Bubble example

What happens if you add event handlers on both the button and its parent element?

<body>
  <div id="container">
    <button>点我!</button>
  </div>
  <pre id="output"></pre>
</body>

Let's try adding click event handlers to the button, its parent div, and the containing body element:

const output = document.querySelector("#output");
function handleClick(e) {
    
    
  output.textContent += `你在 ${
      
      e.currentTarget.tagName} 元素上进行了点击\n`;
}

const container = document.querySelector("#container");
const button = document.querySelector("button");

document.body.addEventListener("click", handleClick);
container.addEventListener("click", handleClick);
button.addEventListener("click", handleClick);

You'll notice that all three elements fire click events when the user clicks the button:

You clicked on the BUTTON element
You clicked on the DIV element
You clicked on the BODY element

in this case:

  • First fire the click event on the button
  • Then the parent element of the button (div element)
  • Then the parent element of the div (body element)

We can describe it like this: Events bubble up from the innermost element that was clicked.

This behavior can be useful, or it can cause unexpected problems. In the next chapters, we'll see a problem it causes and find a solution.

Video player example

In this example, our page contains a video, which is initially hidden; there is also a button labeled "Show Video". We want to have the following interactions:

When the user clicks the "Show Video" button, show the box containing the video, but don't start playing the video.
When the user clicks on the video, the video starts playing.
Hide the box when the user clicks anywhere outside the video inside the box.
HTML code looks like this:

<button>显示视频</button>

<div class="hidden">
  <video>
    <source
      src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm"
      type="video/webm" />
    <p>
      你的浏览器不支持 HTML 视频,这里有视频的<a href="rabbit320.mp4"
        >替代链接</a
      ></p>
  </video>
</div>

It contains:

  • a button element
  • A div element that initially contains the class="hidden" attribute
  • A video element nested inside a div element
    We use CSS to hide the element with the "hidden" class.

JavaScript code looks like this:

const btn = document.querySelector("button");
const box = document.querySelector("div");
const video = document.querySelector("video");

btn.addEventListener("click", () => box.classList.remove("hidden"));
video.addEventListener("click", () => video.play());
box.addEventListener("click", () => box.classList.add("hidden"));

It adds three 'click' event handlers:

  • one on the button which shows the div containing the video
  • one on video, to start playing the video
  • One on a div, for hiding the video
    Let's see how this works:

You should see that when you click the button, both the box and the video it contains are displayed. But when you click on the video, the video starts playing, but the box is hidden again!

The video is inside (is part of) a div, so clicking on the video runs two event handlers at the same time, causing this behavior.

Use stopPropagation() to fix the problem

As we saw in the previous section, event bubbling can sometimes create problems, but there is a way to prevent these problems. Event objects have a function available called stopPropagation() that, when called within an event handler, prevents the event from being propagated to any other elements.

We can fix the current problem by modifying the JavaScript code:

const btn = document.querySelector("button");
const box = document.querySelector("div");
const video = document.querySelector("video");

btn.addEventListener("click", () => box.classList.remove("hidden"));

video.addEventListener("click", (event) => {
    
    
  event.stopPropagation();
  video.play();
});

box.addEventListener("click", () => box.classList.add("hidden"));

What we're doing here is calling stopPropagation() on the event object in the handler for the 'click' event of the video element. This will prevent the event from being passed down the box. Now try to click the button and then click the video.

event capture

Another form of event propagation is event capture. It's like event bubbling, but in reverse order: instead of the event happening first on the innermost target element, then on successively less nested elements, it happens first on the smallest nested element, then Occurs on successively more nested elements until the target is reached.

Event capture is disabled by default, you need to enable it in the capture option of addEventListener().

The following example is similar to the bubbling examples seen earlier, except that the capture option is used:

<body>
  <div id="container">
    <button>点我!</button>
  </div>
  <pre id="output"></pre>
</body>
const output = document.querySelector("#output");
function handleClick(e) {
    
    
  output.textContent += `你在 ${
      
      e.currentTarget.tagName} 元素上进行了点击\n`;
}

const container = document.querySelector("#container");
const button = document.querySelector("button");

document.body.addEventListener("click", handleClick, {
    
     capture: true });
container.addEventListener("click", handleClick, {
    
     capture: true });
button.addEventListener("click", handleClick);

In this case, the order in which the messages appear is reversed: the body event handler fires first, then the div's, and finally the button's:

You clicked on the BODY element
You clicked on the DIV element
You clicked on the BUTTON element

Why use capturing and bubbling together? In the bad old days, when browsers were far less cross-compatible than they are now, Netscape only used event capturing and Internet Explorer only used event bubbling. When the W3C decided to try to standardize the behavior and reach a consensus, they settled on this system that includes both behaviors, which is what modern browsers implement.

By default, almost all event handlers are registered during the bubbling phase, which makes more sense in most cases.

7. Event delegation

In the previous section, we looked at a problem caused by event bubbling and how to fix it. Event bubbling isn't just annoying, though: it can be very useful. In particular, it enables event delegation. In this approach, when we want to run some code when the user interacts with any of a large number of child elements, we set event listeners on their parent elements and let events that happen to them bubble up to them on the parent element instead of having to set event listeners individually on each child element.

Let's go back to the first example, when the user clicks a button, we set the background color of the entire page. Let's say instead that the page is divided into 16 areas and we want to set each area to a random color when the user clicks on it.

Here is the HTML code:

<div id="container">
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
  <div class="tile"></div>
</div>

We have some CSS code to set the size and position of each area:

.tile {
  height: 100px;
  width: 25%;
  float: left;
}

In the JavaScript code, we add click event handlers to each area. However, a simpler and more efficient option is to set a click event handler on the parent node, and rely on event bubbling to ensure that the handler is executed when the user clicks on each area:

function random(number) {
    
    
  return Math.floor(Math.random() * number);
}

function bgChange() {
    
    
  const rndCol = `rgb(${
      
      random(255)}, ${
      
      random(255)}, ${
      
      random(255)})`;
  return rndCol;
}

const container = document.querySelector("#container");

container.addEventListener("click", (event) => {
    
    
  event.target.style.backgroundColor = bgChange();
});

Note: In this example, we use event.target to get the target element of the event (that is, the innermost element). If we want to access the element (container in this example) that handles this event, we can use event.currentTarget.

8. Comprehensive example

Example 1

The following code will change the text inside the button when the button is clicked, and change back to the original when it is clicked again.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Events: Task 1</title>
    <style>
      p {
      
      
        color: purple;
        margin: 0.5em 0;
      }

      * {
      
      
        box-sizing: border-box;
      }

      button {
      
      
        display: block;
        margin: 20px 0 20px 20px;
      }
    </style>
    <link rel="stylesheet" href="../styles.css" />
  </head>

  <body>
    <section class="preview">
    </section>
    <button class="off">按钮</button>

  <script>
    const btn = document.querySelector('.off');
	btn.addEventListener("click",()=>btn.innerText = "按钮" == btn.innerText ? "这是改变文字后的按钮" : "按钮"); 
  </script>

  </body>
</html>

Example 2

By pressing the four WSAD keys on the keyboard, respectively control the movement of the circle up, down, left, and right. The circle is drawn using the function drawCircle(), which takes the following parameters as input:

  • x—the x-coordinate of the circle.
  • y—the y coordinate of the circle.
  • size - the radius of the circle.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Events: Task 2</title>
    <style>
      p {
      
      
        color: purple;
        margin: 0.5em 0;
      }

      * {
      
      
        box-sizing: border-box;
      }

      canvas {
      
      
        border: 1px solid black;
      }
    </style>
    <link rel="stylesheet" href="../styles.css" />
  </head>

  <body>
    <section class="preview">
    </section>
    <canvas width="480" height="320" tabindex="0">
    </canvas>
    
  <script>
    const canvas = document.querySelector('canvas');
    const ctx = canvas.getContext('2d');

    function drawCircle(x, y, size) {
      
      
      ctx.fillStyle = 'white';
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      ctx.beginPath();
      ctx.fillStyle = 'black';
      ctx.arc(x, y, size, 0, 2 * Math.PI);
      ctx.fill();
    }

    let x = 50;
    let y = 50;
    const size = 30;

    drawCircle(x, y, size);

    // Add your code here
    canvas.addEventListener("keypress",(e)=>{
      
      
      if(e.keyCode==65||e.keyCode==97){
      
      //A
        x=x-1;
      }else if(e.keyCode==87||e.keyCode==119){
      
      //w
        y=y-1;
      }else if(e.keyCode==83||e.keyCode==115){
      
      //S
        y=y+1;
      }else if(e.keyCode==68||e.keyCode==100){
      
      //D
        x=x+1;
      }
      drawCircle(x, y, size);
    });
  </script>
  </body>
  
</html>

Example 3

An event listener needs to be set on the button's parent element ( div class="button-bar") which when invoked by clicking any button will set the div tag's background to the button data-color attribute in the color.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Events: Task 3</title>
    <style>
      p {
      
      
        color: purple;
        margin: 0.5em 0;
      }

      * {
      
      
        box-sizing: border-box;
      }

      button {
      
      
        display: block;
        margin: 20px 0 20px 20px;
      }

      .button-bar {
      
      
        padding: 20px 0;
      }
    </style>
    <link rel="stylesheet" href="../styles.css" />
  </head>

  <body>

    <section class="preview">
    </section>
    <div class="button-bar">
      <button data-color="red">Red</button>
      <button data-color="yellow">Yellow</button>
      <button data-color="green">Green</button>
      <button data-color="purple">Purple</button>
    </div>
    
  <script>

  const buttonBar = document.querySelector('.button-bar');

  buttonBar.addEventListener("click",(e)=>{
      
      
    buttonBar.style.backgroundColor = e.target.getAttribute("data-color");
  });

  </script>
  </body>
</html>

reference:

Event IntroductionTest
your skills: Events

Guess you like

Origin blog.csdn.net/qq_33697094/article/details/132302090