UNPKG

@zkochan/pnpm

Version:

A fast implementation of npm install

151 lines (145 loc) 11.5 kB
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="assets/style.css?t=b42a3c4d"> <script src="assets/script.js?t=2bcc4b59"></script> <title>Premise</title> <meta name="viewport" content="width=device-width"> </head> <body class="-menu-visible"> <div class="doc-layout"> <div class="toggle menu-toggle js-menu-toggle"></div> <div class="body page-premise"> <div class="header-nav"> <div class="right"><a href="https://github.com/rstacruz/onmount" data-title="rstacruz/onmount" class="iconlink"> <!-- span.title Open in GitHub--><span class="icon -github"></span></a> </div> </div> <div class="markdown-body"><h1 id="premise">Premise</h1> <p>Onmount is a safe, reliable, idempotent, and testable way to attach JavaScript behaviors to DOM nodes. It&apos;s great for common websites that are not Single-Page Apps, and it goes hand-in-hand with Turbolinks.</p> <h4 id="once-and-only-once">Once and only once</h4> <p>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 <em>once</em> to an element and never again so you don&apos;t get duplicate event handlers and such. This is a concept called <a href="https://en.wikipedia.org/wiki/Idempotence">idempotence</a>.</p> <h4 id="testability">Testability</h4> <p>Onmount allows you to re-run certain behaviors. This is great for making isolated unit tests of your code.</p> <h4 id="reusable">Reusable</h4> <p>Code written in <code>onmount</code> 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.</p> <h2 id="example">Example</h2> <p>Let&apos;s build a navigation with some elements hidden under a <em>more...</em> button. Given this HTML snippet:</p> <pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="pl-ent">div</span> <span class="pl-e">class</span>=<span class="pl-s">&apos;js-expandable-nav&apos;</span>&gt;</span> <span class="hljs-tag">&lt;<span class="pl-ent">a</span> <span class="pl-e">href</span>=<span class="pl-s">&apos;&apos;</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="pl-ent">a</span>&gt;</span> <span class="hljs-tag">&lt;<span class="pl-ent">a</span> <span class="pl-e">href</span>=<span class="pl-s">&apos;&apos;</span>&gt;</span>Inbox<span class="hljs-tag">&lt;/<span class="pl-ent">a</span>&gt;</span> <span class="hljs-tag">&lt;<span class="pl-ent">a</span> <span class="pl-e">href</span>=<span class="pl-s">&apos;&apos;</span>&gt;</span>Messages<span class="hljs-tag">&lt;/<span class="pl-ent">a</span>&gt;</span> <span class="hljs-tag">&lt;<span class="pl-ent">div</span> <span class="pl-e">class</span>=<span class="pl-s">&apos;more&apos;</span>&gt;</span> <span class="hljs-tag">&lt;<span class="pl-ent">a</span> <span class="pl-e">href</span>=<span class="pl-s">&apos;&apos;</span>&gt;</span>Help<span class="hljs-tag">&lt;/<span class="pl-ent">a</span>&gt;</span> <span class="hljs-tag">&lt;<span class="pl-ent">a</span> <span class="pl-e">href</span>=<span class="pl-s">&apos;&apos;</span>&gt;</span>Support<span class="hljs-tag">&lt;/<span class="pl-ent">a</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="pl-ent">div</span>&gt;</span> <span class="hljs-tag">&lt;<span class="pl-ent">button</span>&gt;</span>more...<span class="hljs-tag">&lt;/<span class="pl-ent">button</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="pl-ent">div</span>&gt;</span> </code></pre> <p>To make this work, you&apos;ll typically write JavaScript code like this:</p> <pre><code class="lang-js">$(<span class="hljs-function"><span class="pl-k">function</span> (<span class="hljs-params"></span>) </span>{ $(<span class="pl-s">&apos;.js-expandable-nav button&apos;</span>).on(<span class="pl-s">&apos;click&apos;</span>, <span class="hljs-function"><span class="pl-k">function</span> (<span class="hljs-params"></span>) </span>{ $(<span class="pl-s">&apos;.js-expandable-nav .more&apos;</span>).show() $(<span class="pl-s">&apos;.js-expandable-nav button&apos;</span>).hide() }) }) </code></pre> <h2 id="problems">Problems</h2> <p>However, you have a few problems with this approach.</p> <ul> <li><strong>Not testable</strong> &#x2014; You can&apos;t make unit tests from this code. To test this, you will need to load the entire page, simulate a click, and see if it worked. There&apos;s no easy way to test it in isolation.</li> <li><strong>Not reusable</strong> &#x2014; When there are 2 <code>.js-expandable-nav</code> elements in the page, this will break. This isn&apos;t a concern at first, but it certainly hampers code reusability.</li> <li><strong>Not for dialog boxes</strong> &#x2014; Since it triggers on a <code>document.ready</code> event, it doesn&apos;t work on elements loaded later. There&apos;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).</li> <li><strong>No cleanups</strong> &#x2014; What happens when <code>.js-expandable-nav</code> 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.</li> </ul> <h2 id="solution">Solution</h2> <p>By writing your code in an <code>onmount</code> block, this will solve the issues above. You don&apos;t write your code any differently, but it will be accessible in a way that it&apos;s testable, isolated, and idempotent.</p> <pre><code class="lang-js"><span class="pl-c">/* * attach a behavior to `.js-expandable-nav` */</span> $.onmount(<span class="pl-s">&apos;.js-expandable-nav&apos;</span>, <span class="hljs-function"><span class="pl-k">function</span> (<span class="hljs-params"></span>) </span>{ <span class="pl-k">var</span> $<span class="pl-k">this</span> = $(<span class="pl-k">this</span>) <span class="pl-k">var</span> $button = $<span class="pl-k">this</span>.find(<span class="pl-s">&apos;button&apos;</span>) <span class="pl-k">var</span> $more = $<span class="pl-k">this</span>.find(<span class="pl-s">&apos;.more&apos;</span>) $button.on(<span class="pl-s">&apos;click&apos;</span>, <span class="hljs-function"><span class="pl-k">function</span> (<span class="hljs-params"></span>) </span>{ $more.toggle() $button.hide() }) }) </code></pre> <p>This block is called a <em>behavior</em>&#x2014;that is, a piece of JavaScript that defines what dynamic behavior happens (eg, menu expands on button click) for a given selector (eg, <code>.js-expandable-nav</code>). This is a concept first seen in legacy IE as <a href="https://msdn.microsoft.com/en-us/library/ms531079%28v=vs.85%29.aspx">DHTML behaviors</a>.</p> <pre><code class="lang-js"><span class="pl-c">/* * initializes behaviors on jQuery document.ready, Turbolinks page:change, and * on bootstrap modal show. */</span> $(<span class="pl-c1">document</span>).on(<span class="pl-s">&apos;ready page:change show.bs.modal&apos;</span>, <span class="hljs-function"><span class="pl-k">function</span> (<span class="hljs-params"></span>) </span>{ $.onmount() }) </code></pre> <h2 id="benefits">Benefits</h2> <p>By simply wrapping your code in <code>$.onmount(...)</code> instead of <code>$(function)</code> (aka <code>document.ready</code>), it gives you the power of a few features:</p> <ul> <li><strong>Idempotent</strong> &#x2014; You&apos;re assured that the block will only run once for every <code>.js-expandable-nav</code> element. If it has been applied before, it will not apply again.</li> <li><strong>Reusable</strong> &#x2014; You can retrigger this behavior for tests. This allows you to create an isolated test for the behavior.</li> <li><strong>Reusable</strong> &#x2014; You can call behaviors again and again (<code>$.onmount()</code>) 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.</li> </ul> </div> <div class="footer-nav"> <div class="left"><a href="index.html"><span class="title">onmount</span></a></div> <div class="right"><a href="role.html"><span class="label">Next: </span><span class="title">Role attributes</span></a></div> </div> </div> <div class="menu toc-menu"> <li class="menu-item -level-0 -parent"> <ul class="submenu"> <li class="menu-item -level-1"><a href="index.html" class="link title link-index">onmount</a> </li> <li class="menu-item -level-1"><a href="premise.html" class="link title -active link-premise">Premise</a> <ul class="headings heading-list"> <li class="heading-item -depth-2"><a href="#example" class="hlink link-example">Example</a> </li> <li class="heading-item -depth-2"><a href="#problems" class="hlink link-problems">Problems</a> </li> <li class="heading-item -depth-2"><a href="#solution" class="hlink link-solution">Solution</a> </li> <li class="heading-item -depth-2"><a href="#benefits" class="hlink link-benefits">Benefits</a> </li> </ul> </li> <li class="menu-item -level-1 -parent"><span class="title">Features</span> <ul class="submenu"> <li class="menu-item -level-2"><a href="role.html" class="link title link-role">Role attributes</a> </li> <li class="menu-item -level-2"><a href="cancelling.html" class="link title link-cancelling">Cancelling</a> </li> <li class="menu-item -level-2"><a href="cleanup.html" class="link title link-cleanup">Preforming cleanups</a> </li> <li class="menu-item -level-2"><a href="unique-ids.html" class="link title link-unique-ids">Unique IDs</a> </li> <li class="menu-item -level-2"><a href="idempotency.html" class="link title link-idempotency">Idempotency</a> </li> <li class="menu-item -level-2"><a href="automatic-observation.html" class="link title link-automatic-observation">Automatic observation</a> </li> </ul> </li> <li class="menu-item -level-1 -parent"><span class="title">Testing</span> <ul class="submenu"> <li class="menu-item -level-2"><a href="testing.html" class="link title link-testing">Testing</a> </li> <li class="menu-item -level-2"><a href="debugging.html" class="link title link-debugging">Debugging</a> </li> </ul> </li> <li class="menu-item -level-1 -parent"><span class="title">Integrations</span> <ul class="submenu"> <li class="menu-item -level-2"><a href="turbolinks.html" class="link title link-turbolinks">With Turbolinks</a> </li> <li class="menu-item -level-2"><a href="rails.html" class="link title link-rails">With Rails</a> </li> </ul> </li> </ul> </li> </div> </div> </body> </html>