Onmount is a safe, reliable, idempotent, and testable way to attach JavaScript behaviors to DOM nodes. It’s great for common websites that are not Single-Page Apps, and it goes hand-in-hand with Turbolinks.
It solves many problems that crop up on sites where the DOM changes often, such as when using with Turbolinks or Pjax. Onmount assures you that each behavior is only applied once to an element and never again so you don’t get duplicate event handlers and such. This is a concept called idempotence.
Onmount allows you to re-run certain behaviors. This is great for making isolated unit tests of your code.
Code written in onmount
blocks operate in the context of a single DOM element. It can be applied again other DOM elements later on and will be running in isolation from other instances.
Let’s build a navigation with some elements hidden under a more… button. Given this HTML snippet:
<div class='js-expandable-nav'>
<a href=''>Home</a>
<a href=''>Inbox</a>
<a href=''>Messages</a>
<div class='more'>
<a href=''>Help</a>
<a href=''>Support</a>
</div>
<button>more...</button>
</div>
To make this work, you’ll typically write JavaScript code like this:
$(function () {
$('.js-expandable-nav button').on('click', function () {
$('.js-expandable-nav .more').show()
$('.js-expandable-nav button').hide()
})
})
However, you have a few problems with this approach.
.js-expandable-nav
elements in the page, this will break. This isn’t a concern at first, but it certainly hampers code reusability.document.ready
event, it doesn’t work on elements loaded later. There’s no easy way to retrigger this code, either, which you would want to do for when content is loaded remotely (like in a dialog box)..js-expandable-nav
exits the DOM (eg, the dialog box was closed)? In a more complex scenario, you will want to do cleanup such as unbinding event handlers.By writing your code in an onmount
block, this will solve the issues above. You don’t write your code any differently, but it will be accessible in a way that it’s testable, isolated, and idempotent.
/*
* attach a behavior to `.js-expandable-nav`
*/
$.onmount('.js-expandable-nav', function () {
var $this = $(this)
var $button = $this.find('button')
var $more = $this.find('.more')
$button.on('click', function () {
$more.toggle()
$button.hide()
})
})
This block is called a behavior—that is, a piece of JavaScript that defines what dynamic behavior happens (eg, menu expands on button click) for a given selector (eg, .js-expandable-nav
). This is a concept first seen in legacy IE as DHTML behaviors.
/*
* initializes behaviors on jQuery document.ready, Turbolinks page:change, and
* on bootstrap modal show.
*/
$(document).on('ready page:change show.bs.modal', function () { $.onmount() })
By simply wrapping your code in $.onmount(...)
instead of $(function)
(aka document.ready
), it gives you the power of a few features:
.js-expandable-nav
element. If it has been applied before, it will not apply again.$.onmount()
) every time your DOM changes to make it work for any new elements. This is useful for in-page transitions with Turbolinks or Pjax, or for dynamically-loaded content such as modal boxes.