Preact: Prefixing event handlers (a-la onWebkitAnimationEnd)

Created on 30 Nov 2018  路  8Comments  路  Source: preactjs/preact

The following works great for browsers that don't need a prefix, but fails for browsers that do.

<div onAnimationEnd={this.onAnimationEnd} />

What is the best way to handle this for browsers that need the webkit prefix for this event?

question wontfix workaround

Most helpful comment

You could use a simple helper function to add prefixes:

function addPrefix(name, fn) {
  const renamed = name.charAt(0).toUpperCase() + name.slice(1);
  return {
    [name]: fn,
    ["webkit" + renamed]: fn,
  }
}

// Usage
<div {...addPrefix("onAnimationEnd", this.onAnimationEnd)} />

// output:
<div onAnimationEnd={this.onAnimationEnd} webkitOnAnimationEnd={this.onAnimationEnd} />

All 8 comments

You could use a simple helper function to add prefixes:

function addPrefix(name, fn) {
  const renamed = name.charAt(0).toUpperCase() + name.slice(1);
  return {
    [name]: fn,
    ["webkit" + renamed]: fn,
  }
}

// Usage
<div {...addPrefix("onAnimationEnd", this.onAnimationEnd)} />

// output:
<div onAnimationEnd={this.onAnimationEnd} webkitOnAnimationEnd={this.onAnimationEnd} />

Love it! Here's a vnode hook that will apply it globally & automatically:

import { options } from 'preact';

const mapping = {};
for (let prop in document.body) {
  let prefixedProp = 'webkit' + prop;
  if (prefixedProp in document.body) {
    mapping[prop] = prefixedProp;
  }
}

let old = options.vnode;
options.vnode = vnode => {
  if (typeof vnode.nodeName === 'string') {
    for (let i in vnode.props) {
      let lc = i.toLowerCase();
      if (mapping.hasOwnProperty(lc)) {
        vnode.props[mapping[lc]] = vnode.props[i];
      }
    }
  }
  if (old) old(vnode);
};

I played with this a little bit tonight, but not seeing any of the prefix extensions fire. Tried all of the following variations. The only way I can get a prefix to fire is via addEventListener.

<div
  onwebkitTransitionEnd={() => {
    console.log('onwebkitTransitionEnd'); // never fires
  }}
  onWebkitTransitionEnd={() => {
    console.log('onWebkitTransitionEnd'); // never fires
  }}
  webkitTransitionEnd={() => {
    console.log('webkitTransitionEnd'); // never fires
  }}
  webkitOnTransitionEnd={() => {
    console.log('webkitOnTransitionEnd'); // never fires
  }}
  ref={el => {
    if (el) {
      el.addEventListener('webkitTransitionEnd', () => {
        console.log('addEventListener webkitTransitionEnd'); // does fire
      });
    }
  }}
/>

Ah - it looks like this is a result of webkitTransitionEnd being case-sensitive, whereas transitionend is lowercased. Preact currently has trouble dealing with mixed-case event names (#788).

You could use a global polyfill to emulate transitionend for browsers that only support webkitTransitionEnd:

if (!('ontransitionend' in document.body) {
  addEventListener('webkitTransitionEnd', e => {
    const event = new Event('transitionend', { bubbles: true });
    e.target.dispatch(event);
  });
}

Thanks a ton @developit . This is working well.

My final solution looks something like this:

function getAnimationEndEventName() {
  const animations = {
    animation: 'animationend',
    WebkitAnimation: 'webkitAnimationEnd',
  };

  for (let key in animations) {
    if (document.body.style[key] !== undefined) {
      return animations[key];
    }
  }

  return false;
}

const animationEndEventName = getAnimationEndEventName();
if (animationEndEventName !== 'animationend') {
  window.addEventListener(animationEndEventName, e => {
    const event = new Event('animationend', { bubbles: true });
    e.target.dispatchEvent(event);
  });
}

Love it! Now it's super easy to remove that workaround once browser support requirements eventually shift, without having to change your components 馃憤

Closing, as this isn't an issue with preact 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mizchi picture mizchi  路  3Comments

kay-is picture kay-is  路  3Comments

marcosguti picture marcosguti  路  3Comments

adriaanwm picture adriaanwm  路  3Comments

jescalan picture jescalan  路  3Comments