---
title: Bitovi Academy - Events Part 1
description: Learn the basics of DOM events.
---

- ![AI implementation](https://www.bitovi.com/hubfs/AIConsultingIcon.svg)
  
  [AI implementation](https://www.bitovi.com/services/ai-consulting?hsLang=en-us)
- ![Systems engineering](https://www.bitovi.com/hubfs/icon%20-%20backend.svg)
  
  [Systems engineering](https://www.bitovi.com/services/systems-engineering-consulting?hsLang=en-us)
- ![Project management](https://www.bitovi.com/hubfs/icon%20-%20PM.svg)
  
  [Project management](https://www.bitovi.com/services/agile-project-management-consulting?hsLang=en-us)
- ![Product design](https://www.bitovi.com/hubfs/icon%20-%20design.svg)
  
  [Product design](https://www.bitovi.com/services/product-design-consulting?hsLang=en-us)
- ![Frontend development](https://www.bitovi.com/hubfs/icon%20-%20frontend.svg)
  
  [Frontend development](https://www.bitovi.com/services/frontend-development-consulting?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/digital-consulting-services?hsLang=en-us)

We're Experts in...

- [JavaScript](https://www.bitovi.com/services/frontend/javascript-consulting?hsLang=en-us)
- [AI training](https://www.bitovi.com/ai-training-for-software-engineers?hsLang=en-us)
- [Angular](https://www.bitovi.com/services/frontend/angular-consulting?hsLang=en-us)
- [Design Systems](https://www.bitovi.com/services/axure-figma-migration?hsLang=en-us)
- [React](https://www.bitovi.com/services/frontend/react-consulting?hsLang=en-us)
- [Temporal](https://www.bitovi.com/services/backend/temporal-consulting?hsLang=en-us)
- [React Native](https://www.bitovi.com/services/frontend/react-consulting/react-native?hsLang=en-us)
- [Node.js](https://www.bitovi.com/services/backend/nodejs-consulting-1?hsLang=en-us)

Showcase

![Yum! Brands](https://www.bitovi.com/hubfs/yum-showcase-link-1.png)

[View case study](https://www.bitovi.com/en/bitovi-yum-case-study?hsLang=en-us)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign?hsLang=en-us)
- [![Christie's International Real Estate](https://www.bitovi.com/hubfs/christies.svg)](https://design.bitovi.com/christies)
- [![BAFS](https://www.bitovi.com/hubfs/bafs.svg)](https://www.bitovi.com/ux-design-consulting/ux-case-studies/bafs-ppp?hsLang=en-us)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work?hsLang=en-us)

Open Source Tools

We build powerful tools and open source them to support the community.

[See what we've built →](https://www.bitovi.com/open-source?hsLang=en-us)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about perfecting software delivery
  
  ](https://bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships?hsLang=en-us)
- [![Academy](https://www.bitovi.com/hubfs/limbo/icons/icon-academy.svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy/?hsLang=en-us)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse our tools or contribute to our open source mission.
  
  ](https://www.bitovi.com/open-source?hsLang=en-us)

Let's Connect

- [![Discord](https://www.bitovi.com/hubfs/DiscordLogo.svg)
  
  Discord
  
  ](https://discord.gg/J7ejFsZnJ4)
- [![LinkedIn](https://www.bitovi.com/hubfs/LinkedinLogo.svg)
  
  LinkedIn
  
  ](https://www.linkedin.com/company/bitovi/)
- [![GitHub](https://www.bitovi.com/hubfs/GithubLogo.svg)
  
  GitHub
  
  ](https://github.com/bitovi/)

![Eggbot](https://www.bitovi.com/hubfs/build_assets/bitovi-limbo-cms-react/338/js_client_assets/assets/eggbot-LTGhdSGL.png)

Name *

Work Email *

Phone

What's your project?

Send

### Contact Us

(312) 620-0386contact@bitovi.com

- [ Advanced JavaScript ](https://www.bitovi.com/academy/learn-advanced-javascript.html?hsLang=en-us)
- [ DOM and jQuery ](https://www.bitovi.com/academy/learn-dom-jquery.html?hsLang=en-us) 
    - [ Functional Utilities ](https://www.bitovi.com/academy/learn-dom-jquery/functional-utilities.html?hsLang=en-us)
    - [ Finding Elements ](https://www.bitovi.com/academy/learn-dom-jquery/finding-elements.html?hsLang=en-us)
    - [ Traversing Elements ](https://www.bitovi.com/academy/learn-dom-jquery/traversing-elements.html?hsLang=en-us)
    - [ Attributes and Properties ](https://www.bitovi.com/academy/learn-dom-jquery/attributes-and-properties.html?hsLang=en-us)
    - [ Layout ](https://www.bitovi.com/academy/learn-dom-jquery/layout.html?hsLang=en-us)
    - [ Events Part 1 ](https://www.bitovi.com/academy/learn-dom-jquery/events-part-1.html?hsLang=en-us)
    - [ Events Part 2 ](https://www.bitovi.com/academy/learn-dom-jquery/events-part-2.html?hsLang=en-us)
    - [ Fn Plugins ](https://www.bitovi.com/academy/learn-dom-jquery/fn-plugins.html?hsLang=en-us)
    - [ Tabs Widget ](https://www.bitovi.com/academy/learn-dom-jquery/tabs-widget.html?hsLang=en-us)
- [ Debug JavaScript ](https://www.bitovi.com/academy/learn-to-debug-javascript.html?hsLang=en-us)
- [ Learn TypeScript ](https://www.bitovi.com/academy/learn-typescript.html?hsLang=en-us)
- [ Learn React Native ](https://www.bitovi.com/academy/learn-react-native.html?hsLang=en-us)
- [ Learn Web Components ](https://www.bitovi.com/academy/learn-web-components.html?hsLang=en-us)
- [ Agile Program Management With Jira ](https://www.bitovi.com/academy/learn-agile-program-management-with-jira.html?hsLang=en-us)
- [ Learn RxJS ](https://www.bitovi.com/academy/learn-rxjs.html?hsLang=en-us)
- [ Giving a Training ](https://www.bitovi.com/academy/training.html?hsLang=en-us)
- [ Learn Technology Consulting ](https://www.bitovi.com/academy/learn-technology-consulting.html?hsLang=en-us)
- [ Learn React ](https://www.bitovi.com/academy/learn-react.html?hsLang=en-us)
- [ Learn NgRx ](https://www.bitovi.com/academy/learn-ngrx.html?hsLang=en-us)
- [ Learn Graphql ](https://www.bitovi.com/academy/learn-graphql.html?hsLang=en-us)
- [ Learn Docker ](https://www.bitovi.com/academy/learn-docker.html?hsLang=en-us)
- [ Learn CanJS ](https://www.bitovi.com/academy/learn-canjs.html?hsLang=en-us)
- [ Learn Angular ](https://www.bitovi.com/academy/learn-angular.html?hsLang=en-us)
- [ Advanced TypeScript ](https://www.bitovi.com/academy/advanced-typescript.html?hsLang=en-us)

[View More Courses](https://www.bitovi.com/academy/#courses) [ ]

# Events Part 1 `page`

Learn the basics of DOM events.

## Overview

In this part, we will:

- Learn how to listen to events on an element
- Explore the events the browser supports
- Implement `$.fn.bind` and `$.fn.unbind`

## Video

## Slides

## Setup

Run the following example in CodePen:

```
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css">
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>
<script src="//bitovi.github.io/academy/static/scripts/jquery-test.js"></script>
<link rel="stylesheet" href="//bitovi.github.io/academy/static/scripts/jquery-test.css">
<script type="module">
(function() {
  $ = function(selector) {
    if ( !(this instanceof $) ) {
      return new $(selector);
    }
    var elements;
    if (typeof selector === "string") {
      elements = document.querySelectorAll(selector);
    } else if ($.isArrayLike(selector)) {
      elements = selector;
    }
    [].push.apply(this, elements);
  };

  $.extend = function(target, object) {
    for (var prop in object) {
      if (object.hasOwnProperty(prop)) {
        target[prop] = object[prop];
      }
    }
    return target;
  };

  // Static methods
  $.extend($, {
    isArray: function(obj) {
      return Object.prototype.toString.call(obj) === "[object Array]";
    },
    isArrayLike: function(obj) {
      return obj &&
        typeof obj === "object" &&
        (   obj.length === 0 ||
            typeof obj.length === "number" &&
            obj.length > 0 &&
            obj.length - 1 in obj );

    },
    each: function(collection, cb) {
      if ($.isArrayLike(collection)) {
        for (var i = 0; i < collection.length; i++) {
          if (cb.call(this, i, collection[i]) === false) {
            break;
          }
        }
      } else {
        for (var prop in collection) {
          if (collection.hasOwnProperty(prop)) {
            if (cb.call(this, prop, collection[prop]) === false) {
              break;
            }
          }
        }
      }
      return collection;
    },
    makeArray: function(arr) {
      if ($.isArray(arr)) {
        return arr;
      }
      var array = [];
      $.each(arr, function(i, item) {
        array[i] = item;
      });
      return array;
    },
    proxy: function(fn, context) {
      return function() {
        return fn.apply(context, arguments);
      };
    }
  });

  function makeTraverser(traverser) {
    return function() {
      var elements = [], args = arguments;
      $.each(this, function(i, element) {
        var els = traverser.apply(element, args);
        if ($.isArrayLike(els)) {
          elements.push.apply(elements, els);
        } else if (els) {
          elements.push(els);
        }
      });
      return $(elements);
    };
  }

  $.extend($.prototype, {
    html: function(newHtml) {
      if(arguments.length) {
        return $.each(this, function(i, element) {
          element.innerHTML = newHtml;
        });
      } else {
        return this[0].innerHTML;
      }
    },
    val: function(newVal) {
      if(arguments.length) {
        return $.each(this, function(i, element) {
          element.value = newVal;
        });
      } else {
        return this[0].value;
      }
    },
    text: function(newText) {
      if (arguments.length) {
        return $.each(this, function(i, element) {
          element.textContent = newText;
        });
      } else {
        return this[0].textContent;
      }
    },
    find: makeTraverser(function(selector) {
      return this.querySelectorAll(selector);
    }),
    parent: makeTraverser(function() {
      return this.parentNode;
    }),
    next: makeTraverser(function() {
      return this.nextElementSibling;
    }),
    prev: makeTraverser(function() {
      return this.previousElementSibling;
    }),
    children: makeTraverser(function() {
      return this.children;
    }),
    attr: function(attrName, value) {
      if (arguments.length == 2) {
        return $.each(this, function(i, element) {
          element.setAttribute(attrName, value);
        });
      } else {
        return this[0] && this[0].getAttribute(attrName);
      }
    },
    css: function(cssPropName, value) {
      if (arguments.length == 2) {
        return $.each(this, function(i, element) {
          element.style[cssPropName] = value;
        });
      } else {
        return this[0] &&
          window.getComputedStyle(this[0])
            .getPropertyValue(cssPropName);
      }
    },
    addClass: function(className) {
      return $.each(this, function(i, element) {
        element.classList.add(className);
      });
    },
    removeClass: function(className) {
      return $.each(this, function(i, element) {
        element.classList.remove(className);
      });
    },
    width: function() {
      var paddingLeft = parseInt(this.css("padding-left"), 10),
      paddingRight = parseInt(this.css("padding-right"), 10);
      return this[0].clientWidth - paddingLeft - paddingRight;
    },
    hide: function() {
      return this.css("display", "none");
    },
    show: function() {
      return this.css("display", "");
    },
    offset: function() {
      var offset = this[0].getBoundingClientRect();
      return {
        top: offset.top + window.pageYOffset,
        left: offset.left + window.pageXOffset
      };
    },
    bind: function(eventName, handler){},
    unbind: function(eventName, handler){}
  });

})();
</script>

```

Each exercise builds on the previous exercise. There is a completed solution at the end of this page.

## Events List

The following lists most of the DOM's events and lets you see how they work - Be sure to run in Codepen for a demonstration!

```
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.css" rel="stylesheet"/>
<style>
    .pointer {
        cursor: pointer;
    }
    body, table {
        font-size: 1.1em;
    }
    .event-data {
        font-size: 0.9em;
    }
    .left {
        position: fixed;
        left: 0px; top: 0px; right: 25%; bottom: 0px;
        overflow: auto;
    }
    .right {
        position: fixed;
        right: 0px; top: 0px; left: 75%; bottom: 0px;
        overflow: auto;
    }
</style>
<div class="left">
    <h2>Types of events</h2>
    <form class="submit reset">
        <table class="table">
            <tr>
                <th colspan="2">
                    Mouse Events
                </th>
            </tr>
            <tr>
                <td id="clicked">click</td>
                <td class="click pointer">
                    When a click happens on the same screen location.  Happens after the mouseup.
                </td>
            </tr>
            <tr>
                <td id="dblclicked">dblclick</td>
                <td class="dblclick pointer">
                    When two clicks happen on the same screen location.  Happens after the second click.
                </td>
            </tr>
            <tr>
                <td id="contextmenued">contextmenu</td>
                <td class="contextmenu pointer">
                    Called when the right mouse button is clicked.
                </td>
            </tr>
            <tr>
                <td  id="mousedowned">mousedown</td>
                <td class="mousedown pointer">
                    Called when the mouse is pressed down.
                </td>
            </tr>
            <tr>
                <td  id="mouseuped">mouseup</td>
                <td class="mouseup pointer">
                    Called when a mouse button raises.
                </td>
            </tr>
            <tr>
                <td  id="mouseovered">mouseover</td>
                <td class="mouseover pointer">
                    Called when a mouse moves over an element.
                </td>
            </tr>
            <tr>
                <td  id="mouseouted">mouseout</td>
                <td  class="mouseout pointer">
                    Called when a mouse moves out of an element
                </td>
            </tr>
            <tr>
                <td id="mousemoveed">mousemove</td>
                <td  class="mousemove pointer">
                    Called when a mouse moves at least 1 pixel over an element.
                </td>
            </tr>
            <tr><td id="mouseentered"> mouseenter </td>
                <td class="mouseenter">Called when mouse enters an element from outside elements.</td>
            </tr>
            <tr><td id="mouseleaveed"> mouseleave </td>
                <td class="mouseleave">Called when mouse leaves an element to outside elements.</td>
            </tr>
            <tr><th colspan="2">HTML Events</th></tr>
            <tr>
                <td id="loaded">
                    load
                </td>
                <td>
                    Occurs when the DOM implementation finishes loading all
                    content within a document, all frames within a FRAMESET, or an OBJECT element.
                </td>
            </tr>
            <tr><td id="DOMContentLoadeded">DOMContentLoaded</td>
                <td>Called when the document is ready and parsed, but before images and other resources are loaded</td>
            </tr>
            <tr><td id="unloaded"> unload </td>
                <td>Occurs when the DOM implementation removes a document from a window or frame.</td>
            </tr>
            <tr><td id="beforeunloaded"> beforeunloaded </td>
                <td>Occurs when the DOM implementation removes a document from a window or frame, but allows limited
                JavaScript interaction.</td>
            </tr>
            <tr><td id="aborted"> abort </td>
                <td>Occurs when page loading is stopped before an image has been allowed to completely load.</td>
            </tr>

            <tr><td id="errored"> error </td>
                <td>Occurs when an image does not load properly or when an error occurs during script execution.</td>
            </tr>

            <tr><td id="selected"> select </td>
                <td>
                    Occurs when a user selects some text in a text field. <input class="select" value="select this text"/>
                </td>
            </tr>

            <tr><td id="changeed"> change </td>
                <td>Called after a control loses focus and its value is different.
                    <select  class="change">
                        <option>1</option>
                        <option>2</option>
                        <option>3</option>
                    </select>
                    <input class="change" value="change this text"/>
                    <input type="radio" class="change" name="change_choice" value="1" /> 1 or
                    <input type="radio" class="change" name="change_choice" value="2"/> 2

                    <input type="checkbox" class="change" name="change_choice2" value="1" /> this or
                    <input type="checkbox" class="change" name="change_choice2" value="2"/> that
                </td>
            </tr>
            <tr><td id="reseted"> reset </td>
                <td>
                    Calls when a form is reset. <input type="reset" class="btn btn-default" value="Reset"/>
                </td>
            </tr>
            <tr><td id="submited"> submit </td>
                <td>When a form is submitted, you can return false to cancel.
                    <input type="submit" class="btn btn-default" value="Submit me"/>
                </td>
            </tr>

            <tr><td id="focused"> focus </td>
                <td>
                    <input class="focus" value="focus me"/>
                </td>
            </tr>
            <tr><td id="blured"> blur </td>
                <td>
                    <input class="blur" value="Blur me"/>
                </td>
            </tr>

            <tr><td id="resizeed"> resize </td>
                <td>
                    Occurs when a frameset or document is resized.
                </td>

            </tr>

            <tr><td id="scrolled"> scroll </td>
                <td >
                    <div class="scroll bg-info" style="height: 50px; overflow: auto">
                        <div>Large block<br/>
                        1<br/>2<br/>3<br/>4<br/>5<br/>6<br/>
                        </div>
                    </div>
                </td>
            </tr>
            <tr><td id="hashchangeed"> hashchange </td>
                <td >
                    Happens when the <a href="#hash"><code>#hash</code></a> changes.
                </td>
            </tr>
            <tr>
                <th colspan="2">Key Events</th>
            </tr>

            <tr><td id="keydowned"> keydown </td>
                <td>
                    <input type="input" value="" class="keydown"/>
                </td>
            </tr>
            <tr><td id="keypressed"> keypress </td>
                <td>
                    <input type="input" value="" class="keypress"/>
                </td>
            </tr>
            <tr><td id="keyuped" class="keyup"> keyup </td>
                <td>
                    <input type="input" value="" class="keyup"/>
                </td>
            </tr>
            <tr><td id="inputed" class="input"> input </td>
                <td>
                    <input type="input" value="" class="input"/>
                </td>
            </tr>
            <tr id='pointer-events'>
                <th colspan="2">Pointer Events</th>
            </tr>
            <tr>
                <th colspan="2">Drag Events</th>
            </tr>
            <tr><td id="dragstarted"> dragstart </td>
                <td>
                    <div id="draggable" draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">
                        This div is draggable
                    </div>
                </td>
            </tr>
            <tr><td id="dragended"> dragend </td>
                <td>

                </td>
            </tr>
            <tr><td id="dragentered"> dragenter </td>
                <td>
                    <div class="droppable">Drag Over Me</div>
                </td>
            </tr>

            <tr id="element-other-events">
                <th colspan="2">Other Events On a div element</th>
            </tr>
            <tr id="document-other-events">
                <th colspan="2">Other Events On The document</th>
            </tr>
            <tr id="window-other-events">
                <th colspan="2">Other Events On The window</th>
            </tr>
        </table>
    </form>

    <div style="height: 500px;"></div>
</div>

<div class="right">
    <h2 id="event-name">
        Event data
    </h2>
    <strong>{</strong><br/>
    <ul id="event-data" style="padding-left: 25px;" class="list-unstyled"></ul>
    <strong>}</strong>
</div>

<script type="module">
import "https://unpkg.com/jquery@3/dist/jquery.js";

// Long term, everything should be produced from JavaScript.
// Currently, it is a mix of HTML and JS driving everything.
var events = [
    // Mouseevents
    'click',
    'dblclick',
    'mousedown',
    'mouseup',
    'mouseover',
    'mouseout',
    'mousemove',
    'mouseenter',
    'mouseleave',
    // HTML events
    'contextmenu',
    'select',
    'change',
    'submit',
    'reset',
    'focus',
    'blur',
    'scroll',
    {event: 'load', elements: [window]},
    {event: 'unload', elements: [window]},
    {event: 'beforeunload', elements: [window], handler: function(ev){
        //ev.preventDefault();
        //ev.returnValue = "Are you sure you want to quit?";
    }},
    {event: 'error', elements: [window]},
    {event: 'resize', elements: [window]},
    {event: 'DOMContentLoaded', elements: [document]},
    {event: 'abort', elements: [window]},
    {event: 'hashchange', elements: [window]},

    // Key events
    'keydown',
    'keypress',
    'keyup',
    'input',

    // Dragstart
    {event: 'dragstart', elements: [document], handler(event){
        event.target.style.backgroundColor = "#ff8080";
    }},
    {event: 'dragend', elements: [document], handler(event){
        event.target.style.backgroundColor = "";
    }},
    {event: 'dragenter', elements: [document], handler(event){
        if ( event.target.className == "droppable" ) {
            event.target.style.background = "purple";
        }
    }},

    {event: 'pointercancel', parent: "pointer-events"},
    {event: 'pointermove', parent: "pointer-events"},
    {event: 'pointerout', parent: "pointer-events"},
    {event: 'pointerover', parent: "pointer-events"},
    {event: 'pointerleave', parent: "pointer-events"},
    {event: 'pointerenter', parent: "pointer-events"},
    {event: 'pointerup', parent: "pointer-events"},
    {event: 'pointerdown', parent: "pointer-events"},

    {event: 'fullscreenchange', parent: 'document-other-events', elements: [document]},
    {event: 'fullscreenerror', parent: 'document-other-events', elements: [document]},
];

var detailedEvents = new Set();

var useFilter = true,
    //array of regex: all caps, moz- prefix, webkit- prefix
    filters = [/^([A-Z,_]+)$/, /moz/, /webkit/],
    filterOut = function(s) {
        for(var i = 0; i < filters.length; i++) {
            if(filters[i].test(s)) return true;
        }

        return false;
    },
    lastEvent = null,
    animate = function(selector) {
        $(selector).removeClass()
        .addClass('fadeIn animated bg-success')
        .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() {
            $(selector).removeClass('fadeIn animated');
        });
    },
    printNice = function(value) {
        if(typeof value !== "object" && typeof value !== "function" || (value === null)) {
            return ""+value;
        } else if(typeof value === "function") {
            return "fn(){}"
        } else if(value === window) {
            return "window"
        } else if(value === document){
            return "document"
        } else if(value instanceof HTMLElement) {
            return $("<span>").text("<"+value.nodeName+">")
                .on("mouseenter", function(){
                    $(value).css({backgroundColor: "#ff8080"})
                }).on("mouseleave", function(){
                    $(value).css({backgroundColor: ""})
                });
        } else if(typeof value.length === "number" && (value.length >= 0)) {
            var arr = [].map.call(value, function(value){
                return printNice(value)
            });
            if(arr.length > 2) {
                arr = arr.slice(0,2)
                arr.push("...")
            }
            return "["+arr.join(", ") +"]";
        }

        else {
            return value;
        }
    },
    showEventData = function(evName, ev) {
        lastEvent = {
            name: evName,
            ev: ev
        };
        $("#event-name").text(ev.type+" event:");
        var data = $('#event-data').html('');

        console.log(evName, this, ev);

        for(var k in ev) {
            if(!useFilter || (useFilter && !filterOut(k))) {
                var li = $('<li>').append('<strong>' + k + ':</strong> ')
                    .append(printNice(ev[k]))
                data.append(li);
            }
        }
    };

$('#filter').on('change', function(ev) {
    useFilter = $(this).is(':checked');
    showEventData(lastEvent.name, lastEvent.ev);
});

events.forEach(function(eventData){
    if(typeof eventData === "string") {
        eventData =  {event: eventData, elements: document.getElementsByClassName(eventData) }
    }
    detailedEvents.add(eventData.event);
    var hasRow = $('#' + eventData.event + 'ed').length;
    if(! hasRow && !eventData.elements) {
        $("#"+eventData.parent).after( makeContentForEvent(eventData.event) );
    } else {
        if(!hasRow) {
            $("#"+eventData.parent).after( makeRowContent(eventData.event) );
        }

        [].forEach.call(eventData.elements, function(element){
            element.addEventListener(eventData.event, function(event){
                if(eventData.handler) {
                    eventData.handler.call(this, event);
                }
                showEventData.call(this, eventData.event, event);
                animate('#' + eventData.event + 'ed');
            }, false);
        });
    }

});

function makeRowContent(event) {
    return $(`<tr>
        <td id="${event}ed" class="input"> ${event} </td>
        <td tabindex="0" class='example'>${event}</td>
    </tr>`)
}

function makeContentForEvent(event) {
    var content = makeRowContent(event);

    content.find(".example").on(event, function(e){
        showEventData.call(this, event, e);
        animate('#' + event + 'ed');
    })
    return content;
}

function makeOtherEventsOn(object, appendAfter) {
    var otherEventsList = [];
    for(var property in object) {

        if(property.substr(0,2) === "on" && !detailedEvents.has(property.substr(2))) {
            otherEventsList.push(property.substr(2));
            detailedEvents.add(property.substr(2));
        }
    }
    otherEventsList.sort().reverse().forEach(function(event){
        appendAfter.after( makeContentForEvent(event) );
    });
}
makeOtherEventsOn(document.createElement("div"), $("#element-other-events"));

makeOtherEventsOn(document, $("#document-other-events"));

makeOtherEventsOn(window, $("#window-other-events"));

document.addEventListener("fullscreenchange", function( event ) {

    console.log("CHANGE");

});

</script>

```

## mouseenter vs mouseout

The following shows why mouseenter is usually prefered over mouseover - Be sure to run in Codepen for a demonstration!

```
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
<style>
.vis-mouseenter {
    padding: 20px;
    margin: 10px;
}
</style>
<div class="container-fluid">
    <h3>mouseenter vs mouseover</h3>

    <div class="col-md-4 vis-mouseenter bg-info">
        <strong>Outer</strong>
        mouseenter: <span class="enter">0</span>
        mouseleave: <span class="leave">0</span>

        <div class="vis-mouseenter bg-danger">
            <strong>Inner</strong>
            mouseenter: <span class="enter">0</span>
            mouseleave: <span class="leave">0</span>
        </div>
    </div>

    <div class="col-md-4 vis-mouseenter bg-info">
        <strong>Outer</strong>
        mouseover: <span class="over">0</span>
        mouseout: <span class="out">0</span>

        <div class="vis-mouseenter bg-danger">
            <strong>Inner</strong>
            mouseover: <span class="over">0</span>
            mouseout: <span class="out">0</span>
        </div>
    </div>
</div>

<script type="module">
import "https://unpkg.com/jquery@3/dist/jquery.js";
$.each(['mouseenter', 'mouseleave', 'mouseover', 'mouseout'], function(i, evName) {
    $('.vis-mouseenter').on(evName, function(ev) {
        var el = $(this).children('.' + evName.replace('mouse', '')),
        i = +el.html();

        el.html(++i);
    });
});
</script>

```

## Exercise: `collection.bind` and `collection.unbind`

### The problem

[collection.bind](http://api.jquery.com/bind/) adds an event handler to elements in the collection. [collection.unbind](http://api.jquery.com/unbind/) removes an event handler to elements in the collection.

Click to see test code

```
QUnit.test("$.fn.bind and $.fn.unbind", function () {
  expect(2);

  $("#qunit-fixture").html('<div id="el">text</div>');

  var handler = function (ev) {
    equal(this.nodeName.toLowerCase(), "div", "event called on div");
    equal(ev.type, "click", "click event");
  };

  $("#el").bind("click", handler);

  clickIt($("#el")[0]);

  $("#el").unbind("click", handler);

  clickIt($("#el")[0]);
});
```

### What you need to know

- [addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) adds an event listener to a target element:
  
  ```
  <div>click me</div>
  <script type="module">
    document.body.addEventListener(
      "click",
      function (event) {
        console.log(event.target, "clicked");
      },
      false
    );
  </script>
  ```

- [removeEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener) removes an event listener to a target element. The following removes the `handler` event handler after the first click.
  
  ```
  <div>click me and I log only once</div>
  <script type="module">
    function handler(event) {
      console.log(event.target, "clicked");
      document.body.removeEventListener("click", handler, false);
    }
    document.body.addEventListener("click", handler, false);
  </script>
  ```

## Complete solution

Click to see completed solution

```
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css">
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>
<script src="//bitovi.github.io/academy/static/scripts/jquery-test.js"></script>
<link rel="stylesheet" href="//bitovi.github.io/academy/static/scripts/jquery-test.css">
<script type="module">
(function() {
  $ = function(selector) {
    if ( !(this instanceof $) ) {
      return new $(selector);
    }
    var elements;
    if (typeof selector === "string") {
      elements = document.querySelectorAll(selector);
    } else if ($.isArrayLike(selector)) {
      elements = selector;
    }
    [].push.apply(this, elements);
  };

  $.extend = function(target, object) {
    for (var prop in object) {
      if (object.hasOwnProperty(prop)) {
        target[prop] = object[prop];
      }
    }
    return target;
  };

  // Static methods
  $.extend($, {
    isArray: function(obj) {
      return Object.prototype.toString.call(obj) === "[object Array]";
    },
    isArrayLike: function(obj) {
      return obj &&
        typeof obj === "object" &&
        (   obj.length === 0 ||
            typeof obj.length === "number" &&
            obj.length > 0 &&
            obj.length - 1 in obj );

    },
    each: function(collection, cb) {
      if ($.isArrayLike(collection)) {
        for (var i = 0; i < collection.length; i++) {
          if (cb.call(this, i, collection[i]) === false) {
            break;
          }
        }
      } else {
        for (var prop in collection) {
          if (collection.hasOwnProperty(prop)) {
            if (cb.call(this, prop, collection[prop]) === false) {
              break;
            }
          }
        }
      }
      return collection;
    },
    makeArray: function(arr) {
      if ($.isArray(arr)) {
        return arr;
      }
      var array = [];
      $.each(arr, function(i, item) {
        array[i] = item;
      });
      return array;
    },
    proxy: function(fn, context) {
      return function() {
        return fn.apply(context, arguments);
      };
    }
  });

  function makeTraverser(traverser) {
    return function() {
      var elements = [], args = arguments;
      $.each(this, function(i, element) {
        var els = traverser.apply(element, args);
        if ($.isArrayLike(els)) {
          elements.push.apply(elements, els);
        } else if (els) {
          elements.push(els);
        }
      });
      return $(elements);
    };
  }

  $.extend($.prototype, {
    html: function(newHtml) {
      if(arguments.length) {
        return $.each(this, function(i, element) {
          element.innerHTML = newHtml;
        });
      } else {
        return this[0].innerHTML;
      }
    },
    val: function(newVal) {
      if(arguments.length) {
        return $.each(this, function(i, element) {
          element.value = newVal;
        });
      } else {
        return this[0].value;
      }
    },
    text: function(newText) {
      if (arguments.length) {
        return $.each(this, function(i, element) {
          element.textContent = newText;
        });
      } else {
        return this[0].textContent;
      }
    },
    find: makeTraverser(function(selector) {
      return this.querySelectorAll(selector);
    }),
    parent: makeTraverser(function() {
      return this.parentNode;
    }),
    next: makeTraverser(function() {
      return this.nextElementSibling;
    }),
    prev: makeTraverser(function() {
      return this.previousElementSibling;
    }),
    children: makeTraverser(function() {
      return this.children;
    }),
    attr: function(attrName, value) {
      if (arguments.length == 2) {
        return $.each(this, function(i, element) {
          element.setAttribute(attrName, value);
        });
      } else {
        return this[0] && this[0].getAttribute(attrName);
      }
    },
    css: function(cssPropName, value) {
      if (arguments.length == 2) {
        return $.each(this, function(i, element) {
          element.style[cssPropName] = value;
        });
      } else {
        return this[0] &&
          window.getComputedStyle(this[0])
            .getPropertyValue(cssPropName);
      }
    },
    addClass: function(className) {
      return $.each(this, function(i, element) {
        element.classList.add(className);
      });
    },
    removeClass: function(className) {
      return $.each(this, function(i, element) {
        element.classList.remove(className);
      });
    },
    width: function() {
      var paddingLeft = parseInt(this.css("padding-left"), 10),
      paddingRight = parseInt(this.css("padding-right"), 10);
      return this[0].clientWidth - paddingLeft - paddingRight;
    },
    hide: function() {
      return this.css("display", "none");
    },
    show: function() {
      return this.css("display", "");
    },
    offset: function() {
      var offset = this[0].getBoundingClientRect();
      return {
        top: offset.top + window.pageYOffset,
        left: offset.left + window.pageXOffset
      };
    },
    bind: function(eventName, handler) {
      return $.each(this, function(i, element) {
        element.addEventListener(eventName, handler, false);
      });
    },
    unbind: function(eventName, handler) {
      return $.each(this, function(i, element) {
        element.removeEventListener(eventName, handler, false);
      });
    },
  });

})();
</script>

```

###### On this page...

###### Get help

If you need [help](https://discord.gg/J7ejFsZnJ4) with an exercise, please reach out to us on [Discord](https://discord.gg/J7ejFsZnJ4)

If you find a bug, please [create an issue](https://github.com/bitovi/academy/issues/new) or email [contact@bitovi.com](mailto:contact@bitovi.com?Subject=Training%20Suggestion)

## Join the Bitovi Newsletter

 Do you want to receive updates about new Bitovi Academy courses, Bitovi open source projects, and dev community news? If so, please provide your email. We won’t spam you or give it away for any reason!  
*By providing your email you agree to receive communications from Bitovi*

![](https://bitovi.github.io/academy/static/img/close.svg)

```json
{
     "@context": "http://schema.org",
     "@type": "Organization",
     "mainEntityOfPage":{
          "@type":"WebPage",
          "@id":"https://www.bitovi.com/academy/learn-dom-jquery/events-part-1.html",
          "description": "Learn the basics of DOM events. 
"
     },
     "url": "http://bitovi.com",
     "logo": "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg",
     "image": "https://www.bitovi.com/hubfs/bitovi-logo-x2.png",
     "name": "Bitovi Web App Consulting",
     "alternateName": "Bitovi",
      "naics": "541511",
      "description": "Bitovi is a UX, UI design and front-end JavaScript development consulting company",
     "address": {
          "@type": "PostalAddress",
          "streetAddress": "1134 Pine Tree Lane ",
          "addressLocality": "Libertyville",
          "addressRegion": "IL",
          "addressCountry": "United States",
          "postalCode": "60048"
     },
     "areaServed": {
          "@type": "GeoCircle",
          "geoMidpoint": {
               "@type": "GeoCoordinates",
               "latitude": "41.8781",
               "longitude": "87.6298"
          },
          "geoRadius": "5000 km"
     },
     "telephone": "312-620-0386",
     "email": "contact@bitovi.com",
     "sameAs": [
          "https://www.facebook.com/BitoviLLC/",
          "https://twitter.com/bitovi",
          "https://www.linkedin.com/company/bitovi"
     ]
}
```