---
title: Faster jQuery.event.fix with ES5 Getters
description: Faster jQuery.event.fix with ES5 Getters
image: https://www.bitovi.com/hubfs/js-banner.jpg
---

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

We're Experts in...

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

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)

More Projects

- [![Levi's](https://www.bitovi.com/hubfs/levis.svg)](https://www.bitovi.com/web-application-consulting-work/levis-ecommerce-responsive-redesign)
- [![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)
- [View more
  
  →
  
  ](https://www.bitovi.com/our-software-consulting-work)

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)

- [![Blog](https://www.bitovi.com/hubfs/icon%20-%20blog.svg)
  
  BlogWe post about delivering products and solving problems.
  
  ](https://www.bitovi.com/blog)
- [![Partnerships](https://www.bitovi.com/hubfs/Handshake-1.svg)
  
  PartnershipsLearn about Bitovi's technology partners
  
  ](https://www.bitovi.com/partnerships)
- [![Academy](https://www.bitovi.com/hubfs/icon%20-%20academy%20(4).svg)
  
  AcademyFree courses to build delivery skills
  
  ](https://www.bitovi.com/academy)
- [![Open source tools](https://www.bitovi.com/hubfs/icon%20-%20open%20source.svg)
  
  Open source toolsUse or contribute to our community
  
  ](https://www.bitovi.com/open-source)

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

 April 13, 2012

# Faster jQuery.event.fix with ES5 Getters

 Faster jQuery.event.fix with ES5 Getters

![Justin Meyer](https://www.bitovi.com/hubfs/justin.jpeg)

 Justin Meyer

Share:

[![Twitter](https://www.bitovi.com/hubfs/limbo-generated/_astro/twitter-white.os3xLc3C_Z2nW4or.svg) ](https://twitter.com/intent/tweet?text=) [![Reddit](https://www.bitovi.com/hubfs/limbo-generated/imgs/icons/reddit.png) ](http://reddit.com/submit?url=)

If you turn on a profiler in most of the apps we've built and click around like a user, after a while you'll notice `jQuery.event.fix` is often taking the most time (in the [video](http://www.youtube.com/watch?v=GQOWUyatVTU) below, it takes 6.34% of the total time). Following the logic behind [Amdahl's law](http://en.wikipedia.org/wiki/Amdahl's_law), it makes sense that making `jQuery.event.fix` faster would have the greatest impact on our apps. This article walks through:

- how jQuery normalizes events
- why it has been slow
- the ways it has been sped up, and
- how using ES5 getters could speed it up even more.

### How jQuery Normalizes Events

When an event is received by jQuery, it **normalizes** the event properties before it [dispatches](https://github.com/jquery/jquery/blob/master/src/event.js#L385) the event to registered event handlers. By **normalizing**, I mean it makes sure the event handler properties are the same across all browsers. For example, IE does not support `event.relatedTarget`, instead IE provides `event.toElement` and `event.fromElement`. jQuery uses those properties to set a `relatedTarget` property.

It might surprise you, but your event handlers aren't receiving a real event. Instead they are getting a new [`jQuery.Event`](https://github.com/jquery/jquery/blob/master/src/event.js#L644) with similar properties to a raw HTML event. jQuery does this because it can't set properties on a raw HTML event.

You can get the raw event with `originalEvent` like:

```
$("#clickme").bind("click", function( ev ) {
  ev.originalEvent
})
```

jQuery creates and normalizes the `jQuery.Event` from the raw event in `jQuery.event.fix`.

### Why fix has been slow

Calling fix slow is inaccurate. In my [basic test](http://jsperf.com/jquery-event-fix/3), fix can be called 50,000 times a second in Chrome - that's blazin. However, in most apps, events are involved in almost every execution path. This means `jQuery.event.fix` is called pretty much every time anything happens.

`jQuery.event.fix` works by copying each property of the raw HTML event to the newly minted `jQuery.Event`. This copying is where almost all of the expense comes from `jQuery.event.fix`.

[I posted about this](http://forum.jquery.com/topic/fix-is-slow) 2 years ago on jQuery's forums. [Dave Methvin](https://twitter.com/#!/davemethvin) suggested using ES5 getters to avoid looking up the properties. Mike Helgeson made a run at it, but nothing came out of it.

### How it has been sped up

For jQuery 1.7, Dave Methvin improved jQuery.event.fix considerably. It copies and normalizes only the event properties that are needed. It also uses a fast loop:

```
for ( i = copy.length; i; ) {
  prop = copy[ --i ];
  event[ prop ] = originalEvent[ prop ];
}
```

But it's still the slowest part of our apps. The following video shows Austin clicking around like a user in one of our apps with the profiler on. At the end of this speed up video, you'll see `jQuery.event.fix` is the slowest method of the app at **6.34%**!

Speeding up `jQuery.event.fix` would have a big impact across the application. And, it can be done in one place.

### Using ES5 getters

ES5 getters allow `jQuery.event.fix` to avoid copying every property and normalizing it for every event. Instead getters can do this on-demand. That is, they can lookup the `originalEvent`'s value and normalize it if needed.

For example, the following defines a relatedTarget getter on `jQuery.Event`s:

```
Object.defineProperty(jQuery.Event.prototype, "relatedTarget",{
  get : function(){
    var original = this.originalEvent;
    return original.relatedTarget ||
           original.fromElement === this.target ?
             original.toElement :
             original.fromElement;
  }
})
```

`jQuery.event.fix` could be changed to set up the jQuery.Event with the originalEvent, src, and target property like:

```
$.event.fix = function(event){
  // make sure the event has not already been fixed
  if ( event[ jQuery.expando ] ) {
    return event;
  }
  // Create a jQuery event with at minimum a target and type set
  var originalEvent = event,
      event = jQuery.Event( originalEvent );

  event.target = originalEvent.target;
  // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
  if ( !event.target ) {
    event.target = originalEvent.srcElement || document;
  }

  // Target should not be a text node (#504, Safari)
  if ( event.target.nodeType === 3 ) {
    event.target = event.target.parentNode;
  }

  return event;
}
```

**Note**: `jQuery.Event( originalEvent )` set the originalEvent and src properties. We set target because target is almost always going to be used.

When `event.relatedTarget` is called it calls the getter and returns the normalized value. We could add every property this way.

**But there's a catch!**

I brought this up to jQuery-maintainer and chief Rick Waldron and [he shared this with me](https://twitter.com/#!/rwaldron/status/190282642594349056):

> fun fact: getters are atrociously slow. http://jsperf.com/object-create-prop-attribs/2 This will likely never be in jQuery.

Buzz kill! Fortunately, we can be smart and cache the computed value for quick lookup the next time. My first naive try was like:

```
Object.defineProperty(jQuery.Event.prototype, "relatedTarget",{
  get : function(){
    var original = this.originalEvent;
    return this.relatedTarget = (original.relatedTarget ||
           original.fromElement === this.target ?
             original.toElement :
             original.fromElement);
  }
})
```

Notice the `this.relatedTarget = ...`. I was hoping this would set a `relatedTarget` property on the `jQuery.Event` instance. This does not work because accessor descriptors are not writeable. But, we can use `Object.defineProperty` to set a data descriptor on the event instance like:

```
Object.defineProperty(jQuery.Event.prototype, "relatedTarget",{
  get : function(){
    var original = this.originalEvent,
    value =  (original.relatedTarget ||
              original.fromElement === this.target ?
                original.toElement :
                original.fromElement);
    Object.defineProperty(this, "relatedTarget",{
      value: value
    });
    return value;
  }
})
```

[The final code](https://gist.github.com/2377196) goes through the list of properties that `jQuery.event.fix` copies:

- `$.event.keyHooks.props`
- `$.event.mouseHooks.props`
- `$.event.props`

and creates getters for each one. In the getter, it checks if that prop is special (needs normalizing) and uses that prop's special function to normalize the value. It then uses the `defineProperty`-value trick to cache the result for fast lookup.

I created a [basic JSPerf](http://jsperf.com/jquery-event-fix/3) that shows a 3 to 4 times performance improvement. It compares my fix method vs jQuery's existing fix method and reads the event's `pageX` and `pageY` twice.

## Conclusions

My measurement results are not perfect:

- Although, the profiler indicates `jQuery.event.fix` is the slowest (speed x #-of-calls) part of our app, it does not count DOM interactions. It also betrays the fact that `jQuery.event.fix` is almost never the slowest part of any one user interaction.
- The JSPerf only reads 2 properties. For a proper evaluation, a graph should be made of performance vs the number of properties read.

Despite this, from a library's perspective, improving `jQuery.event.fix` should be an easy and high-value target for jQuery. A simple change could improve our app's over-all performance by almost 3%. There are very few improvements in jQuery that could claim something similar.

[![Tag for open source](https://www.bitovi.com/hubfs/limbo/icons/tag.svg) open source ](https://www.bitovi.com/blog/topic/open-source)[![Tag for development](https://www.bitovi.com/hubfs/limbo/icons/tag.svg) development ](https://www.bitovi.com/blog/topic/development)

 Previous Post

![Avoid the Memory Leak Zombie Apocalypse](https://www.bitovi.com/hs-fs/hubfs/Avoid%20the%20Memory%20Leak%20Zombie%20Apocalypse.png?height=117&name=Avoid%20the%20Memory%20Leak%20Zombie%20Apocalypse.png) [ Avoid the Memory Leak Zombie Apocalypse ](https://www.bitovi.com/blog/avoid-the-zombie-apocalypse)

  

 Next Post

![canjs-background-blue-1](https://www.bitovi.com/hs-fs/hubfs/canjs-background-blue-1.png?height=117&name=canjs-background-blue-1.png) [ Hashchange Routing with can.route - Part 1 - Basics ](https://www.bitovi.com/blog/hashchange-routing-with-can-route-part-1-basics)

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

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Justin Meyer"
  },
  "dateModified" : "November 29, 2018, 6:22:49 PM",
  "datePublished" : "2012-04-13 20:25:28",
  "description" : "Faster jQuery.event.fix with ES5 Getters",
  "headline" : "Faster jQuery.event.fix with ES5 Getters",
  "image" : {
    "@type" : "ImageObject",
    "url" : "https://www.bitovi.com/hubfs/js-banner.jpg"
  },
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "url" : "https://www.bitovi.com/hubfs/bitovi-logo-23-1.svg"
    },
    "name" : "Bitovi"
  }
}
```