--- title: 'Introducing Ember Modifiers' date: 2020-03-03 tags: - Ember - Tech description: >- The less awkward Ember inline helper and proper home for handling all of those DOM events. --- As a frontend developer I find myself doing plenty of UX-centric work that involves A11y, clear visual feedback based on user interactions, and at times creating new user interactions altogether. On prior versions of Ember this may have been handled by a Component API such as `keypress(event) {`... or, was it `keyPress(event) {`? All of that is in the past as we introduce [Ember Modifiers](https://github.com/ember-modifier/ember-modifier). ## What is an Ember Modifier? `ember-modifier` is a library [referenced in the Ember Guides while discussing event handling](https://guides.emberjs.com/release/components/template-lifecycle-dom-and-modifiers/#toc_event-handlers). Modifiers are [a new feature introduced in Ember Octane](https://blog.emberjs.com/2019/03/06/coming-soon-in-ember-octane-part-4.html) Modifiers are [a new feature introduced in Ember Octane](https://blog.emberjs.com/2019/03/06/coming-soon-in-ember-octane-part-4.html). In a nutshell, the next time you reach for a `didInsertElement()` with an `addEventListener()` consider any of the following examples instead. ## Using Ember Modifiers ### Getting Started First we install the library ```bash # In Your Terminal ember install ember-modifier ``` ### Handling Dom Events Below is an example for how to track the focus state of a DOM element. {% raw %} ```html {{!-- my-component.hbs --}} ``` {% endraw %} ```js // my-component.js import Component from '@glimmer/component'; import { tracked } from "@glimmer/tracking"; import { action } from "@ember/object"; export default class MyComponent extends Component { @tracked myButtonHasFocus = false; @action handleFocus() { this.myButtonHasFocus = true; } @action handleBlur({ target, relatedTarget }) { if (!target.contains(relatedTarget)) this.myButtonHasFocus = false; } } ``` ### Handling Key Presses We can create a custom modifier like so: ```bash # In Your Terminal ember g modifier key-down ``` ```js // modifiers/key-down.js import { modifier } from 'ember-modifier'; export default modifier(function keyUp(element, [handler], { key: desiredKey }) { let keydownListener = (evt) => { if (!desiredKey || desiredKey === evt.key) { handler(evt); } } element.addEventListener('keydown', keydownListener); return () => { element.removeEventListener('keydown', keydownListener); } }); ``` {% raw %} ```js // tests/integration/modifiers/key-down-test.js import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import { render, triggerKeyEvent } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; import { set } from '@ember/object'; module('Integration | Modifier | key-down', function(hooks) { setupRenderingTest(hooks); test('it fires off a function when a key down, passing the event along with it', async function(assert) { set(this, 'keyDown', ({ key }) => { assert.step('key down'); assert.equal(key, 'Enter'); }); await render(hbs`
`); await triggerKeyEvent('[data-test-id=keydown]', 'keydown', 'Enter'); assert.verifySteps(['key down']); }); test('it can listen for a specific key', async function(assert) { set(this, 'keyDown', ({ key }) => { assert.step('enter key down'); assert.equal(key, 'Enter'); }); await render(hbs`
`); await triggerKeyEvent('[data-test-id=keydown]', 'keydown', 'Enter'); await triggerKeyEvent('[data-test-id=keydown]', 'keydown', 'Spacebar'); assert.verifySteps(['enter key down']); }); }); ``` #### Leveraging a key-down modifier ##### "Binding" a key to an action A simple example of a focusable element listening for the Enter key to be pressed. ```html {{!-- my-component.hbs --}}