<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Actualize - Medium]]></title>
        <description><![CDATA[Coding resources written by Actualize alumni and staff - Medium]]></description>
        <link>https://medium.com/actualize-network?source=rss----bac00d3a330d---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Actualize - Medium</title>
            <link>https://medium.com/actualize-network?source=rss----bac00d3a330d---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 27 May 2026 23:55:06 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/actualize-network" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Modern HTML Explained For Dinosaurs]]></title>
            <link>https://medium.com/actualize-network/modern-html-explained-for-dinosaurs-65e56af2981?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/65e56af2981</guid>
            <category><![CDATA[html]]></category>
            <category><![CDATA[css]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Wed, 14 Nov 2018 21:35:17 GMT</pubDate>
            <atom:updated>2018-11-14T21:33:01.054Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EEHKg3z_FAk0ipZDFuWKcw.png" /></figure><p>Out of the three main frontend technologies (HTML, CSS, and JavaScript), HTML has remained the most consistent. If your only concern was creating content, an HTML document from the 1990s would look pretty similar to one created in 2018:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;utf-8&quot;&gt;<br>    &lt;title&gt;My test page&lt;/title&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;h1&gt;Hello there!&lt;/h1&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>You have elements with tags and content, tags with attributes — other than the simplified <a href="https://en.wikipedia.org/wiki/Document_type_declaration">doctype</a> on the first line, not much has changed! However, over the years web development has taken a dramatic shift from creating static web sites (with a focus on content) to creating dynamic web applications (with a focus on interaction)— something the web wasn’t originally designed to do. Creating custom user interfaces that are still semantic and accessible, improving performance using attributes and tooling, organizing code for reuse and maintainability — there are now a whole new set of concerns at play.</p><p>The goal of this article is to provide a historical context of how HTML has evolved to the language it is today in 2018. We’ll start with the fundamentals of well structured and accessible HTML, just like the dinosaurs of old. Then we’ll introduce different techniques to improve performance, responsiveness, and maintainability. CSS and JavaScript will inevitably make its way into this conversation; for the purposes of this article, they will be covered from the perspective of how they impact the writing of HTML itself. By understanding this history, you will be able to take full advantage of both old and new features of the language that can often be overlooked. Let’s get started!</p><h3>Writing content with semantic elements</h3><p>Let’s add some more content to the earlier HTML example. For now we’ll make a basic website with a navigation section with links and a search input, a large showcase section for displaying general site info (often referred to as a hero section or <a href="https://en.wikipedia.org/wiki/Jumbotron">jumbotron</a>), three column sections for articles, and a footer section for copyright info.</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;utf-8&quot;&gt;<br>    &lt;title&gt;My test page&lt;/title&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    <strong>&lt;div class=&quot;navbar&quot;&gt;<br>      &lt;ul&gt;<br>        &lt;li&gt;<br>          &lt;a href=&quot;#&quot;&gt;Home&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li&gt;<br>          &lt;a href=&quot;#&quot;&gt;Info&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li&gt;<br>          &lt;a href=&quot;#&quot;&gt;About&lt;/a&gt;<br>        &lt;/li&gt;<br>      &lt;/ul&gt;<br>      &lt;form&gt;<br>        &lt;input type=&quot;text&quot; placeholder=&quot;Search&quot;&gt;<br>        &lt;button type=&quot;submit&quot;&gt;Search&lt;/button&gt;<br>      &lt;/form&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;main&quot;&gt;<br>      &lt;div class=&quot;hero&quot;&gt;<br>        &lt;h1&gt;Hello there!&lt;/h1&gt;<br>        &lt;p&gt;General info about the page goes here&lt;/p&gt;<br>        &lt;p&gt;&lt;a href=&quot;#&quot;&gt;Learn more&lt;/a&gt;&lt;/p&gt;<br>      &lt;/div&gt;<br>      &lt;div class=&quot;grid&quot;&gt;<br>        &lt;div class=&quot;column&quot;&gt;<br>          &lt;h2&gt;First Heading&lt;/h2&gt;<br>          &lt;p&gt;Article content goes here&lt;/p&gt;<br>          &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>        &lt;/div&gt;<br>        &lt;div class=&quot;column&quot;&gt;<br>          &lt;h2&gt;Second Heading&lt;/h2&gt;<br>          &lt;p&gt;Article content goes here&lt;/p&gt;<br>          &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>        &lt;/div&gt;<br>        &lt;div class=&quot;column&quot;&gt;<br>          &lt;h2&gt;Third Heading&lt;/h2&gt;<br>          &lt;p&gt;Article content goes here&lt;/p&gt;<br>          &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>        &lt;/div&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;footer&quot;&gt;<br>      &lt;p&gt;Copyright info goes here&lt;/p&gt;<br>    &lt;/div&gt;</strong><br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>Here we’re using basic elements with tags like &lt;div&gt;, &lt;h1&gt;, &lt;h2&gt;, &lt;p&gt;, etc., to mark the content. The HTML here works, but it’s not fully <a href="https://en.wikipedia.org/wiki/Semantic_HTML">semantic</a> — that is to say, the tags do not communicate the meaning of the content as well as they could.</p><p>When HTML5 was introduced in 2008, it provided new elements to improve document semantics. Here’s an attempt to make the HTML more semantic (with changes in bold):</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html <strong>lang=&quot;en&quot;</strong>&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;utf-8&quot;&gt;<br>    <strong>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;<br>    &lt;meta name=&quot;description&quot; content=&quot;&quot;&gt;<br>    &lt;meta name=&quot;author&quot; content=&quot;&quot;&gt;</strong><br>    &lt;title&gt;My test page&lt;/title&gt;<br>    <strong>&lt;!--[if lt IE 9]&gt;<br>      &lt;script src=&quot;</strong><a href="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"><strong>https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js</strong></a><strong>&quot;&gt;&lt;/script&gt;<br>    &lt;![endif]--&gt;</strong><br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;<strong>nav role=&quot;navigation&quot;</strong>&gt;<br>      &lt;ul&gt;<br>        &lt;li&gt;<br>          &lt;a href=&quot;#&quot;&gt;Home&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li&gt;<br>          &lt;a href=&quot;#&quot;&gt;Info&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li&gt;<br>          &lt;a href=&quot;#&quot;&gt;About&lt;/a&gt;<br>        &lt;/li&gt;<br>      &lt;/ul&gt;<br>      &lt;form&gt;<br>        &lt;input type=&quot;text&quot; placeholder=&quot;Search&quot;&gt;<br>        &lt;button type=&quot;submit&quot;&gt;Search&lt;/button&gt;<br>      &lt;/form&gt;<br>    &lt;/<strong>nav</strong>&gt;<br>    &lt;<strong>main role=&quot;main&quot;</strong>&gt;<br>      &lt;<strong>section</strong> class=&quot;hero&quot;&gt;<br>        &lt;h1&gt;Hello there!&lt;/h1&gt;<br>        &lt;p&gt;General info about the page goes here&lt;/p&gt;<br>        &lt;p&gt;&lt;a href=&quot;#&quot;&gt;Learn more&lt;/a&gt;&lt;/p&gt;<br>      &lt;/<strong>section</strong>&gt;<br>      &lt;<strong>section</strong> class=&quot;grid&quot;&gt;<br>        &lt;<strong>article</strong> class=&quot;column&quot;&gt;<br>          &lt;h2&gt;First Heading&lt;/h2&gt;<br>          &lt;p&gt;Article content goes here&lt;/p&gt;<br>          &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>        &lt;/<strong>article</strong>&gt;<br>        &lt;<strong>article</strong> class=&quot;column&quot;&gt;<br>          &lt;h2&gt;Second Heading&lt;/h2&gt;<br>          &lt;p&gt;Article content goes here&lt;/p&gt;<br>          &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>        &lt;/<strong>article</strong>&gt;<br>        &lt;<strong>article</strong> class=&quot;column&quot;&gt;<br>          &lt;h2&gt;Third Heading&lt;/h2&gt;<br>          &lt;p&gt;Article content goes here&lt;/p&gt;<br>          &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>        &lt;/<strong>article</strong>&gt;<br>      &lt;/<strong>section</strong>&gt;<br>    &lt;/<strong>main</strong>&gt;<br>    &lt;<strong>footer role=&quot;contentinfo&quot;</strong>&gt;<br>      &lt;p&gt;Copyright info goes here&lt;/p&gt;<br>    &lt;/<strong>footer</strong>&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>Let’s examine some of the these changes:</p><ul><li>The &lt;html lang=&quot;en&quot;&gt; specifies the language of the document, which helps search engines and browsers identify the appropriate content.</li><li>The additional &lt;meta&gt; tags provides information about the site not directly visible on page, used by search engines and other services. They also provide information about how to size the content on different devices.</li><li>The &lt;nav&gt;, &lt;main&gt;, &lt;section&gt;, &lt;article&gt;, and &lt;footer&gt; tags provide more accessible structure to the HTML document (compared to generic &lt;div&gt; tags). These tags were introduced with HTML5.</li><li>The &lt;!-- [if lt IE 9]&gt;...--&gt; comment adds a JavaScript file that only gets loaded for older versions of Internet Explorer, since they don’t support the above HTML5 tags. Note that many recent websites don’t include that comment these days, as fewer sites support those browsers.</li><li>The role attributes provide accessibility information as well. Note that using a &lt;nav&gt; tag generally is enough for accessibility, adding the extra role=&quot;navigation&quot; is used in case the &lt;nav&gt; tag isn’t recognized.</li></ul><p>Writing semantic HTML can seem unimportant, especially when it doesn’t affect the visual appearance of a website. However, your website is being viewed by more than just human beings — web browsers, search engines, screen readers all depend on semantic HTML to function properly.</p><h3>Improving accessibility with WAI-ARIA attributes</h3><p>Let’s look at the search input in the navbar from our example above:</p><pre>      &lt;form&gt;<br>        &lt;input type=&quot;text&quot; placeholder=&quot;Search&quot;&gt;<br>        &lt;button type=&quot;submit&quot;&gt;Search&lt;/button&gt;<br>      &lt;/form&gt;</pre><p>This input element uses a placeholder attribute instead of a label element to let the user know its purpose. This works for humans, but it’s not properly semantic HTML, and can be missed by screen readers and other technologies. The way to make this accessible is to use an aria-label attribute:</p><pre>      &lt;form&gt;<br>        &lt;input type=&quot;text&quot; placeholder=&quot;Search&quot; <strong>aria-label=&quot;Search&quot;</strong>&gt;<br>        &lt;button type=&quot;submit&quot;&gt;Search&lt;/button&gt;<br>      &lt;/form&gt;</pre><p>WAI-ARIA, which stands for <em>“Web Accessibility Initiative — Accessible Rich Internet Applications”</em> (often simply ARIA for short), is a set of attributes that makes HTML more accessible when semantic tags aren’t enough. The role attribute seen in the previous section is an ARIA attribute. So far these attributes seem like minor changes, but it becomes much more important as we use HTML for things beyond basic documents.</p><p>Let’s look at a more complex example — say we want to add some tabbed content in our HTML to provide instructions on how to install some program on Windows, Mac, and Linux. Since there’s no native way of building tabs in HTML, we have to make do with things like unordered lists, links, and divs to construct our own tab markup. Here we can use accessibility attributes such as role, aria-controls, aria-selected, and aria-labelledby to mark up the HTML, as seen below:</p><pre>&lt;ul <strong>role=&quot;tablist&quot;</strong>&gt;<br>  &lt;li&gt;<br>    &lt;a id=&quot;windows-tab&quot; href=&quot;#windows&quot; <strong>role=&quot;tab&quot; aria-controls=&quot;windows&quot; aria-selected=&quot;true&quot;</strong>&gt;Windows&lt;/a&gt;<br>  &lt;/li&gt;<br>  &lt;li&gt;<br>    &lt;a id=&quot;mac-tab&quot; href=&quot;#mac&quot; <strong>role=&quot;tab&quot; aria-controls=&quot;mac&quot; aria-selected=&quot;false&quot;</strong>&gt;Mac&lt;/a&gt;<br>  &lt;/li&gt;<br>  &lt;li&gt;<br>    &lt;a id=&quot;linux-tab&quot; href=&quot;#linux&quot; <strong>role=&quot;tab&quot; aria-controls=&quot;linux&quot; aria-selected=&quot;false&quot;</strong>&gt;Linux&lt;/a&gt;<br>  &lt;/li&gt;<br>&lt;/ul&gt;</pre><pre>&lt;div&gt;<br>  &lt;div id=&quot;windows&quot; <strong>role=&quot;tabpanel&quot;</strong> <strong>aria-labelledby=&quot;windows-tab&quot;</strong>&gt;<br>    &lt;img src=&quot;<a href="http://1000logos.net/wp-content/uploads/2017/04/Microsoft-Logo.png">http://1000logos.net/wp-content/uploads/2017/04/Microsoft-Logo.png</a>&quot; alt=&quot;microsoft logo&quot;&gt;<br>    ...<br>  &lt;/div&gt;<br>  &lt;div id=&quot;mac&quot; <strong>role=&quot;tabpanel&quot;</strong> <strong>aria-labelledby=&quot;mac-tab&quot;</strong>&gt;<br>    &lt;img src=&quot;<a href="https://i.ytimg.com/vi/ipOzBWuYZvg/maxresdefault.jpg">https://i.ytimg.com/vi/ipOzBWuYZvg/maxresdefault.jpg</a>&quot; alt=&quot;apple logo&quot;&gt;<br>    ...<br>  &lt;/div&gt;<br>  &lt;div id=&quot;linux&quot; <strong>role=&quot;tabpanel&quot;</strong> <strong>aria-labelledby=&quot;linux-tab&quot;</strong>&gt;<br>    &lt;img src=&quot;<a href="https://noware.tech/wp-content/uploads/sites/140/2018/04/linux-1024x565.jpg">https://noware.tech/wp-content/uploads/sites/140/2018/04/linux-1024x565.jpg</a>&quot; alt=&quot;linux logo&quot;&gt;<br>    ...<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><p>Without these accessibility attributes, the tabbed content would have no discernible relationship with the tab controls. Having these attributes helps screen readers identify content, enables keyboard shortcuts with proper tabbing, etc. Using appropriate ARIA attributes can be a whole study in and of itself — for a deeper dive, check out the <a href="https://www.w3.org/TR/wai-aria-practices-1.1/">official guidelines</a>.</p><p>All this may seem like a lot of work just to improve the accessibility of a website. It’s important to acknowledge accessibility as an integral part of the web, which was designed as a platform to freely share information with everyone, not just a select few. Making a website accessible improves the experience for every visitor — for example, accessible keyboard shortcuts help those who can never use a mouse, those who temporarily can’t use a mouse, and those who prefer to not use a mouse (a.k.a., most programmers). Accessibility can be easy to overlook when dealing with other features, but it should not be ignored.</p><blockquote>If you’re interested in improving the accessibility of your website, there are checklists such as <a href="https://a11yproject.com/checklist">this one</a> from the <a href="https://a11yproject.com/">A11Y project</a>, which is a great place to start. However, making a website accessible isn’t purely about checking off items — it’s something that can always be improved, like any aspect of user experience. <strong>The best way to make your website accessible is to actually use your website as different people in your audience would</strong> — test it out using a screen reader, try only using a keyboard instead of a mouse, view your website using a <a href="https://www.toptal.com/designers/colorfilter">color blind filter</a>, etc.</blockquote><h3>Making it pretty with CSS and JavaScript</h3><p>If we take a look at the website so far, it looks pretty bare as you would expect:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VCXY_h9ynSUJ_9piFx9ATA.png" /></figure><p>In order to spruce it up, we’ll add a CSS file to apply styling. Now if you’re not particularly good at CSS, it could take you many days to make this website look pretty. Instead of writing your own CSS, you could always use a CSS framework, which is essentially CSS that someone else wrote in a reusable manner.</p><p>One popular CSS framework is Bootstrap, which came out in 2011 and quickly became adopted and used by <a href="https://www.ostraining.com/blog/coding/bootstrap-popularity/">literally millions of websites</a>. Let’s see what the code would look like using Bootstrap (changes in bold):</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;utf-8&quot;&gt;<br>    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;<br>    &lt;meta name=&quot;description&quot; content=&quot;&quot;&gt;<br>    &lt;meta name=&quot;author&quot; content=&quot;&quot;&gt;<br>    &lt;title&gt;My test page&lt;/title&gt;<br>    <strong>&lt;link rel=&quot;stylesheet&quot; href=&quot;</strong><a href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"><strong>https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css</strong></a><strong>&quot; integrity=&quot;sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO&quot; crossorigin=&quot;anonymous&quot;&gt;</strong><br>    &lt;!--[if lt IE 9]&gt;<br>      &lt;script src=&quot;<a href="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js">https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js</a>&quot;&gt;&lt;/script&gt;<br>    &lt;![endif]--&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;nav <strong>class=&quot;navbar navbar-expand-lg navbar-light bg-light&quot;</strong> role=&quot;navigation&quot;&gt;<br>      &lt;ul <strong>class=&quot;navbar-nav mr-auto&quot;</strong>&gt;<br>        &lt;li <strong>class=&quot;nav-item active&quot;</strong>&gt;<br>          &lt;a <strong>class=&quot;nav-link&quot;</strong> href=&quot;#&quot;&gt;Home&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li <strong>class=&quot;nav-item&quot;</strong>&gt;<br>          &lt;a <strong>class=&quot;nav-link&quot;</strong> href=&quot;#&quot;&gt;Info&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li <strong>class=&quot;nav-item&quot;</strong>&gt;<br>          &lt;a <strong>class=&quot;nav-link&quot;</strong> href=&quot;#&quot;&gt;About&lt;/a&gt;<br>        &lt;/li&gt;<br>      &lt;/ul&gt;<br>      &lt;form <strong>class=&quot;form-inline my-2 my-lg-0&quot;</strong>&gt;<br>        &lt;input <strong>class=&quot;form-control mr-sm-2&quot;</strong> type=&quot;text&quot; placeholder=&quot;Search&quot; aria-label=&quot;Search&quot;&gt;<br>        &lt;button <strong>class=&quot;btn btn-outline-success my-2 my-sm-0&quot; </strong>type=&quot;submit&quot;&gt;Search&lt;/button&gt;<br>      &lt;/form&gt;<br>    &lt;/nav&gt;<br>    &lt;main role=&quot;main&quot;&gt;<br>      &lt;section <strong>class=&quot;jumbotron&quot;</strong>&gt;<br>        &lt;h1 <strong>class=&quot;display-4&quot;</strong>&gt;Hello there!&lt;/h1&gt;<br>        &lt;p <strong>class=&quot;lead&quot;</strong>&gt;General info about the page goes here&lt;/p&gt;<br>        &lt;p&gt;&lt;a <strong>class=&quot;btn btn-primary btn-lg&quot;</strong> href=&quot;#&quot;&gt;Learn more&lt;/a&gt;&lt;/p&gt;<br>      &lt;/section&gt;<br>      &lt;section <strong>class=&quot;container&quot;</strong>&gt;<br>        <strong>&lt;div class=&quot;row&quot;&gt;</strong><br>          &lt;article <strong>class=&quot;col-4&quot;</strong>&gt;<br>            &lt;h2&gt;First Heading&lt;/h2&gt;<br>            &lt;p&gt;Article content goes here&lt;/p&gt;<br>            &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>          &lt;/article&gt;<br>          &lt;article <strong>class=&quot;col-4&quot;</strong>&gt;<br>            &lt;h2&gt;Second Heading&lt;/h2&gt;<br>            &lt;p&gt;Article content goes here&lt;/p&gt;<br>            &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>          &lt;/article&gt;<br>          &lt;article <strong>class=&quot;col-4&quot;</strong>&gt;<br>            &lt;h2&gt;Third Heading&lt;/h2&gt;<br>            &lt;p&gt;Article content goes here&lt;/p&gt;<br>            &lt;ul <strong>class=&quot;nav nav-tabs&quot;</strong> role=&quot;tablist&quot;&gt;<br>              &lt;li <strong>class=&quot;nav-item&quot;</strong>&gt;<br>                &lt;a <strong>class=&quot;nav-link active&quot; data-toggle=&quot;tab&quot;</strong> id=&quot;windows-tab&quot; href=&quot;#windows&quot; role=&quot;tab&quot; aria-controls=&quot;windows&quot; aria-selected=&quot;true&quot;&gt;Windows&lt;/a&gt;<br>              &lt;/li&gt;<br>              &lt;li <strong>class=&quot;nav-item&quot;</strong>&gt;<br>                &lt;a <strong>class=&quot;nav-link&quot; data-toggle=&quot;tab&quot;</strong> id=&quot;mac-tab&quot; href=&quot;#mac&quot; role=&quot;tab&quot; aria-controls=&quot;mac&quot; aria-selected=&quot;false&quot;&gt;Mac&lt;/a&gt;<br>              &lt;/li&gt;<br>              &lt;li <strong>class=&quot;nav-item&quot;</strong>&gt;<br>                &lt;a <strong>class=&quot;nav-link&quot; data-toggle=&quot;tab&quot;</strong> id=&quot;linux-tab&quot; href=&quot;#linux&quot; role=&quot;tab&quot; aria-controls=&quot;linux&quot; aria-selected=&quot;false&quot;&gt;Linux&lt;/a&gt;<br>              &lt;/li&gt;<br>            &lt;/ul&gt;<br>            &lt;div <strong>class=&quot;tab-content&quot;</strong>&gt;<br>              &lt;div <strong>class=&quot;tab-pane fade show active&quot;</strong> id=&quot;windows&quot; role=&quot;tabpanel&quot; aria-labelledby=&quot;windows-tab&quot;&gt;<br>                &lt;img <strong>class=&quot;img-fluid&quot;</strong> src=&quot;<a href="http://1000logos.net/wp-content/uploads/2017/04/Microsoft-Logo.png">http://1000logos.net/wp-content/uploads/2017/04/Microsoft-Logo.png</a>&quot; alt=&quot;microsoft logo&quot;&gt;<br>                ...<br>              &lt;/div&gt;<br>              &lt;div <strong>class=&quot;tab-pane fade&quot;</strong> id=&quot;mac&quot; role=&quot;tabpanel&quot; aria-labelledby=&quot;mac-tab&quot;&gt;<br>                &lt;img <strong>class=&quot;img-fluid&quot;</strong> src=&quot;<a href="https://i.ytimg.com/vi/ipOzBWuYZvg/maxresdefault.jpg">https://i.ytimg.com/vi/ipOzBWuYZvg/maxresdefault.jpg</a>&quot; alt=&quot;apple logo&quot;&gt;<br>                ...<br>              &lt;/div&gt;<br>              &lt;div <strong>class=&quot;tab-pane fade&quot;</strong> id=&quot;linux&quot; role=&quot;tabpanel&quot; aria-labelledby=&quot;linux-tab&quot;&gt;<br>                &lt;img <strong>class=&quot;img-fluid&quot;</strong> src=&quot;<a href="https://noware.tech/wp-content/uploads/sites/140/2018/04/linux-1024x565.jpg">https://noware.tech/wp-content/uploads/sites/140/2018/04/linux-1024x565.jpg</a>&quot; alt=&quot;linux logo&quot;&gt;<br>                ...<br>              &lt;/div&gt;<br>            &lt;/div&gt;<br>            &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>          &lt;/article&gt;<br>        <strong>&lt;/div&gt;</strong><br>      &lt;/section&gt;<br>    &lt;/main&gt;<br>    &lt;footer <strong>class=&quot;navbar navbar-dark bg-secondary&quot; </strong>role=&quot;contentinfo&quot;&gt;<br>      &lt;p&gt;Copyright info goes here&lt;/p&gt;<br>    &lt;/footer&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>Let’s examine the changes:</p><ul><li>The &lt;link&gt; in the &lt;head&gt; tag adds the Bootstrap CSS to our site. Note that we’re linking to a file that’s hosted online, which can open up some <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity">security risks</a> — the integrity and crossorigin attributes help ensure the file being linked to is correct.</li><li>The classes that were added are all specific to Bootstrap — the Bootstrap CSS that was added has styles which target those specific class names with the specific HTML structure.</li><li>One extra &lt;div class=&quot;row&quot;&gt; was added to surround the three articles to take advantage of <a href="https://getbootstrap.com/docs/4.1/layout/grid/">Bootstrap’s grid layout system</a> (which uses this specific HTML structure).</li></ul><p>Here’s what the site looks like now:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fO1wXfiWfdv8vY3_qeQv0A.png" /></figure><p>Not bad! Note that in order to use a CSS framework like Bootstrap, you actually don’t need to write any CSS at all to get started — you just need to add the appropriate classes to the HTML to take advantage of the CSS that comes with the framework.</p><p>One thing to note here is that while the tabs are styled properly (only one tab is visible at any moment), they don’t work properly yet — clicking on a tab doesn’t do anything. That’s because in this case this type of custom interaction isn’t handled by CSS — it’s handled by JavaScript. In this case, we can get the Bootstrap tabs to work by adding the JavaScript files that comes with the Bootstrap framework in the &lt;head&gt; tag:</p><pre>&lt;script src=&quot;<a href="https://code.jquery.com/jquery-3.3.1.slim.min.js">https://code.jquery.com/jquery-3.3.1.slim.min.js</a>&quot; integrity=&quot;sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</pre><pre>&lt;script src=&quot;<a href="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js">https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js</a>&quot; integrity=&quot;sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</pre><pre>&lt;script src=&quot;<a href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js">https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js</a>&quot; integrity=&quot;sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</pre><p>The last script is Bootstrap’s JavaScript. The first two are dependencies that Bootstrap uses (jQuery and Popper), which must be loaded first before the Bootstrap script is loaded. If you check out this <a href="https://codepen.io/peterxjang/pen/WKYxWL?editors=1000#">live example</a>, you can see that the tabs now work!</p><p>Bootstrap became widely used because it helped with the major pain points of CSS at the time, such as browser inconsistencies and a lack of a proper grid system. There are some downsides of using CSS frameworks like Bootstrap — in particular, they can be difficult to customize compared to writing CSS from scratch, which can make your website seem generic compared to others.</p><p>Also, with the rise of smartphones and mobile traffic, reducing CSS and JS file sizes became increasingly important — anything above a few kilobytes can significantly affect performance on slower internet connections. The way we’re using Bootstrap in the above example, we’re requiring the user to download the entire Bootstrap framework with the site, even though we’re only using a couple of styles and features. In the next section, we’ll take a look at several techniques to help tackle these performance issues.</p><blockquote>Note: A solid grasp of CSS and JavaScript is intrinsically linked to making sophisticated websites with HTML; however, a deep dive into these languages is outside the scope of this article. If you’d like to learn more about the fundamentals of CSS and JavaScript, the <a href="https://developer.mozilla.org/en-US/">MDN Web Docs</a> are always a good place to start. If you want to get a better understanding of how all the newer features of CSS (flexbox, grid, SASS, etc.) fit together with all the tooling and techniques involved with it, check out my article <a href="https://medium.com/actualize-network/modern-css-explained-for-dinosaurs-5226febe3525">Modern CSS Explained For Dinosaurs</a>.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ytCirVt0CqnH8wNUX_6vcA.png" /></figure><h3>Improving performance with HTML attributes</h3><p>At this point we have a website with reasonably well organized, semantic HTML. And if this is all we are considering, then our website would be done! Yet there are many areas where the site can be improved, in terms of both performance (how quickly the website loads for a user) and maintainability (how easy the code is to change for a developer).</p><h4>The defer attribute for scripts</h4><p>For our website, one major optimization is to address the JavaScript files that are being loaded in the header. These files are large enough to actually slow down the website. In order to render a page, a web browser reads the given HTML and converts it into a format it understands — the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction">Document Object Model</a>, or DOM. As you might expect, a web browser starts at the top of the HTML document and works its way down. That means if it sees a &lt;script&gt; tag, it will download and execute the script before moving on to the next line. You can see an illustration of this process here:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*qo7CQsQCd-QYJchbCXLj_w.png" /><figcaption>(From <a href="https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/">hacks.mozilla.org</a>)</figcaption></figure><p>A common optimization hack is to move all JavaScript &lt;script&gt; tags out of the &lt;head&gt; tag and into the end of the &lt;body&gt; tag. You can see this in Bootstrap’s own <a href="https://getbootstrap.com/docs/4.1/getting-started/introduction/#starter-template">starter template</a>:</p><pre>&lt;!doctype html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    &lt;!-- Required meta tags --&gt;<br>    &lt;meta charset=&quot;utf-8&quot;&gt;<br>    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;</pre><pre>    &lt;!-- Bootstrap CSS --&gt;<br>    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css&quot; integrity=&quot;sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO&quot; crossorigin=&quot;anonymous&quot;&gt;</pre><pre>    &lt;title&gt;Hello, world!&lt;/title&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;h1&gt;Hello, world!&lt;/h1&gt;</pre><pre>    &lt;!-- Optional JavaScript --&gt;<br>    &lt;!-- jQuery first, then Popper.js, then Bootstrap JS --&gt;<br><strong>    &lt;script src=&quot;https://code.jquery.com/jquery-3.3.1.slim.min.js&quot; integrity=&quot;sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;<br>    &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js&quot; integrity=&quot;sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;<br>    &lt;script src=&quot;https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js&quot; integrity=&quot;sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</strong><br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>This a optimization is a hack in the sense that this isn’t how HTML was designed to be organized — CSS and JavaScript files are meant to be defined in the &lt;head&gt; tag as seen earlier. However, keeping everything in the &lt;head&gt; tag creates the unintentional side effect of slowing page rendering performance. Moving all &lt;script&gt; tags to the bottom of the &lt;body&gt; tag is one way to improve performance.</p><p>In 2018, many websites still use this trick of moving all &lt;script&gt; tags to the bottom of the &lt;body&gt; tag. However, there is a less hacky approach that has been supported by browsers for nearly 10 years — the defer attribute. By adding this attribute to a &lt;script&gt; tag, the browser will download the external file without blocking the rest of the DOM from being built, and will execute the script after the DOM building is complete. You can see an illustration of this process here:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*GhpdG7JTaVJ6nbbaVLkSLg.png" /><figcaption>(From <a href="https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/">hacks.mozilla.org</a>)</figcaption></figure><p>In many cases, keeping &lt;script&gt; tags in the &lt;head&gt; with a defer attribute would result in faster loading pages, since the files can be downloaded in parallel with the DOM being built. This is what Bootstrap’s starter template would look like using the defer attribute instead:</p><pre>&lt;!doctype html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    &lt;!-- Required meta tags --&gt;<br>    &lt;meta charset=&quot;utf-8&quot;&gt;<br>    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;</pre><pre>    &lt;!-- Bootstrap CSS --&gt;<br>    &lt;link rel=&quot;stylesheet&quot; href=&quot;https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css&quot; integrity=&quot;sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO&quot; crossorigin=&quot;anonymous&quot;&gt;</pre><pre>    &lt;!-- Optional JavaScript --&gt;<br>    &lt;!-- jQuery first, then Popper.js, then Bootstrap JS --&gt;<br>    &lt;script <strong>defer </strong>src=&quot;https://code.jquery.com/jquery-3.3.1.slim.min.js&quot; integrity=&quot;sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;<br>    &lt;script <strong>defer </strong>src=&quot;https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js&quot; integrity=&quot;sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;<br>    &lt;script <strong>defer </strong>src=&quot;https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js&quot; integrity=&quot;sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</pre><pre>    &lt;title&gt;Hello, world!&lt;/title&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;h1&gt;Hello, world!&lt;/h1&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>The benefit here is that the website would render faster, and it keeps the HTML organized with scripts in the &lt;head&gt; tag as intended. For more fine tuned control over what files get downloaded and executed in what order, there is also the async attribute as well as the rel=&quot;preload&quot; attribute for &lt;link&gt; tags (which you can read more about <a href="https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/">here</a>).</p><h4>The srcset attribute for images</h4><p>For our website, one other major performance optimization would be the images. Right now the images are being “hotlinked”, which means they are being directly linked off someone else’s website. Not only is this problematic from a maintenance perspective (if the other person ever changes their image it will break our website), it can also be problematic from a performance perspective.</p><p>Instead of directly linking the files, we can download the files and link to them locally. Furthermore, we can optimize the image file sizes by resizing them to an appropriate resolution. Instead of this image tag which directly links to a single image on another website:</p><pre>&lt;img class=&quot;img-fluid&quot; src=&quot;<a href="http://1000logos.net/wp-content/uploads/2017/04/Microsoft-Logo.png">http://1000logos.net/wp-content/uploads/2017/04/Microsoft-Logo.png</a>&quot; alt=&quot;microsoft logo&quot;&gt;</pre><p>We can create multiple versions of the image locally and link to them responsively:</p><pre>&lt;img class=&quot;img-fluid&quot; <strong>src=&quot;microsoft-logo-small.png&quot;</strong> <strong>srcset=&quot;microsoft-logo-medium.png 1000w, microsoft-logo-large.png 2000w&quot;</strong> alt=&quot;microsoft logo&quot;&gt;</pre><p>Here we’re using a small, medium, and large version of the logo. The srcset attribute tells the browser to load the appropriate version based on the browser width. The srcset attribute was introduced around 2013, but it took several years for browsers to fully support it. As of 2018 it has <a href="https://caniuse.com/#feat=srcset">pretty decent browser support</a>, so it’s definitely worth making part of your workflow.</p><p>Optimizing image sizes is often the largest performance gain for many websites — image download sizes are often orders of magnitude larger than any JavaScript and CSS files. You can get even finer control over images with <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">the </a><a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">&lt;picture&gt; element</a>; however, using simple srcset attributes <a href="https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-srcset/">is often more than enough</a> for most use cases.</p><h4>Other HTML attributes</h4><p>As a language, HTML has <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes">many attributes</a> and continues to add new ones that can be used to improve performance (like importance and lazyload). While it can be daunting, focusing on the largest resources in terms of download size (images and scripts), whatever they may be for your particular website, is usually the best place to start.</p><blockquote>Note that just like accessibility, there is no blanket set of performance rules that will always work for every website — you should benchmark your website to determine what works best (browsers such as <a href="https://developers.google.com/web/tools/lighthouse/">Chrome</a> and <a href="https://developer.mozilla.org/en-US/docs/Tools/Performance">Firefox</a> provide such tools). Again, <strong>the best approach is to simply use your website with slow network conditions</strong> (which browser’s dev tools can simulate) — if you use your own website for even just a week under slow network conditions, you’re likely to find a multitude of performance fixes to improve its experience.</blockquote><h3>Improving performance with tooling</h3><p>So far we’ve been using the tools that the HTML language provides for optimizing performance. You can also use external tools for even more performance benefits. Let’s take a look at several commonly used approaches.</p><h4>Code minification</h4><p>One important performance optimization is <strong>minification</strong> (sometimes referred to as uglification) of your JavaScript and CSS code. This involves using a program to analyze and remove unnecessary or redundant data from your code, from simple things like removing unneeded spaces to complex things like renaming long variables to single characters when possible. Here’s an example from the first JavaScript minifier <a href="http://www.crockford.com/javascript/jsmin.html">released in 2003</a> by Douglas Crockford. The sample unminified code looks like this:</p><pre>// is.js<br><br>// (c) 2001 Douglas Crockford<br>// 2001 June 3<br><br><br>// is<br><br>// The -is- object is used to identify the browser.  Every browser edition<br>// identifies itself, but there is no standard way of doing it, and some of<br>// the identification is deceptive. This is because the authors of web<br>// browsers are liars. For example, Microsoft&#39;s IE browsers claim to be<br>// Mozilla 4. Netscape 6 claims to be version 5.<br><br>var is = {<br>    ie:      navigator.appName == &#39;Microsoft Internet Explorer&#39;,<br>    java:    navigator.javaEnabled(),<br>    ns:      navigator.appName == &#39;Netscape&#39;,<br>    ua:      navigator.userAgent.toLowerCase(),<br>    version: parseFloat(navigator.appVersion.substr(21)) ||<br>             parseFloat(navigator.appVersion),<br>    win:     navigator.platform == &#39;Win32&#39;<br>}<br>is.mac = is.ua.indexOf(&#39;mac&#39;) &gt;= 0;<br>if (is.ua.indexOf(&#39;opera&#39;) &gt;= 0) {<br>    is.ie = is.ns = false;<br>    is.opera = true;<br>}<br>if (is.ua.indexOf(&#39;gecko&#39;) &gt;= 0) {<br>    is.ie = is.ns = false;<br>    is.gecko = true;<br>}</pre><p>And the code after minification looks like this:</p><pre>var is={ie:navigator.appName==&#39;Microsoft Internet Explorer&#39;,java:navigator.javaEnabled(),ns:navigator.appName==&#39;Netscape&#39;,ua:navigator.userAgent.toLowerCase(),version:parseFloat(navigator.appVersion.substr(21))||parseFloat(navigator.appVersion),win:navigator.platform==&#39;Win32&#39;}<br>is.mac=is.ua.indexOf(&#39;mac&#39;)&gt;=0;if(is.ua.indexOf(&#39;opera&#39;)&gt;=0){is.ie=is.ns=false;is.opera=true;}<br>if(is.ua.indexOf(&#39;gecko&#39;)&gt;=0){is.ie=is.ns=false;is.gecko=true;}</pre><p>These changes do add up — from our earlier example, the unminified bootstrap.js has a size of 124 KB, whereas the minified bootstrap.min.js has a size of 51 KB. That’s less than half the original size — by <a href="https://www.youtube.com/watch?v=kgv7U3GYlDY">Grabthar’s hammer</a>, what a savings! But seriously, it makes a significant difference in terms of how quickly a website will download and display, especially on slower internet connections.</p><p>The Bootstrap CSS and JavaScript we’re using in our previous example is already minified, but if you want to minify your own code, you could use online tools such as <a href="https://javascript-minifier.com/">JavaScript Minifier</a> or <a href="https://www.minifier.org/">Minify</a>, there are plenty to choose from. Or you would use a command line tool, which saves you the process of copying your code into a website.</p><h4>File concatenation</h4><p>Another related performance optimization is <strong>concatenation</strong>, which is converting multiple JavaScript files (or CSS files) into a single file. Browsers can download a single file faster than downloading multiple smaller files, based on the HTTP/1.1 protocol that browsers have used since 1999.</p><p>It’s important to note that the new version of the protocol, HTTP/2, was released in 2015 and potentially changes this optimization. HTTP/2 allows multiple simultaneous connections, so in theory it’s better to have multiple small files as opposed to one large concatenated file. However, in practice <a href="http://engineering.khanacademy.org/posts/js-packaging-http2.htm">it doesn’t appear to be quite so simple</a>, as there are still important benefits from concatenation. As of 2018, it is still common practice to concatenate JavaScript and CSS files.</p><p>To concatenate your files, you <em>theoretically</em> could do it manually — copy the contents of each JavaScript file into a single file, repeat for CSS files, etc. Then modify the HTML to link to the single concatenated JavaScript file and the single concatenated CSS file. You’d have to do this each time you deploy your app, which would be quite painful to maintain — it’s much more preferable to use some automated process (more on that below).</p><h4>Critical CSS</h4><p>One more optimization popularized in recent years is inlining the <strong>critical CSS</strong> for a page. This involves using a tool to identify all the HTML elements that the user would first see when going to a web page:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/944/1*eqFRbhOchUMNGLj9lisEqQ.jpeg" /><figcaption>From <a href="https://www.smashingmagazine.com/2015/08/understanding-critical-css/">https://www.smashingmagazine.com/2015/08/understanding-critical-css/</a></figcaption></figure><p>Once these HTML elements are identified, the tool would find all the CSS that affect those elements and add them directly to the HTML file. In this way, the browser is able to display a fully styled website without having to wait for the remaining CSS to download!</p><p>There are different tools that help you identify your critical CSS, from the node-based <a href="https://github.com/addyosmani/critical">critical</a> library by Addy Osmani to the web based <a href="https://jonassebastianohlsson.com/criticalpathcssgenerator/">Critical Path CSS Generator</a> by Jonas Ohlsson Aden. Below is an example of what the HTML from our previous Bootstrap example would look like after being analyzed by the <a href="https://github.com/addyosmani/critical">critical</a> tool (changes in bold):</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;utf-8&quot;&gt;<br>    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;<br>    &lt;meta name=&quot;description&quot; content=&quot;&quot;&gt;<br>    &lt;meta name=&quot;author&quot; content=&quot;&quot;&gt;<br>    &lt;title&gt;My test page&lt;/title&gt;<br>    <strong>&lt;style&gt;<br>      :root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,&quot;Segoe UI&quot;,Roboto,&quot;Helvetica Neue&quot;,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;,&quot;Segoe UI Symbol&quot;,&quot;Noto Color Emoji&quot;;--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,&quot;Liberation Mono&quot;,&quot;Courier New&quot;,monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar}@-ms-viewport{width:device-width}article,footer,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,&quot;Segoe UI&quot;,Roboto,&quot;Helvetica Neue&quot;,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;,&quot;Segoe UI Symbol&quot;,&quot;Noto Color Emoji&quot;;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}h1,h2{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}ul{margin-top:0;margin-bottom:1rem}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}img{vertical-align:middle;border-style:none}button{border-radius:0}button,input{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button{text-transform:none}[type=submit],button{-webkit-appearance:button}[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}h1,h2{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}h1{font-size:2.5rem}h2{font-size:2rem}.lead{font-size:1.25rem;font-weight:300}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}.img-fluid{max-width:100%;height:auto}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}</strong><a href="http://twitter.com/media"><strong>@media</strong></a><strong> (min-width:576px){.container{max-width:540px}}</strong><a href="http://twitter.com/media"><strong>@media</strong></a><strong> (min-width:768px){.container{max-width:720px}}</strong><a href="http://twitter.com/media"><strong>@media</strong></a><strong> (min-width:992px){.container{max-width:960px}}</strong><a href="http://twitter.com/media"><strong>@media</strong></a><strong> (min-width:1200px){.container{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.col-4{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.form-control{display:block;width:100%;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem}.form-control::-ms-expand{background-color:transparent;border:0}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}</strong><a href="http://twitter.com/media"><strong>@media</strong></a><strong> (min-width:576px){.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.fade:not(.show){opacity:0}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.tab-content&gt;.tab-pane{display:none}.tab-content&gt;.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}</strong><a href="http://twitter.com/media"><strong>@media</strong></a><strong> (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .active&gt;.nav-link{color:rgba(0,0,0,.9)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}</strong><a href="http://twitter.com/media"><strong>@media</strong></a><strong> (min-width:576px){.jumbotron{padding:4rem 2rem}}.bg-secondary{background-color:#6c757d!important}.bg-light{background-color:#f8f9fa!important}.my-2{margin-top:.5rem!important}.my-2{margin-bottom:.5rem!important}.mr-auto{margin-right:auto!important}</strong><a href="http://twitter.com/media"><strong>@media</strong></a><strong> (min-width:576px){.my-sm-0{margin-top:0!important}.my-sm-0{margin-bottom:0!important}.mr-sm-2{margin-right:.5rem!important}}</strong><a href="http://twitter.com/media"><strong>@media</strong></a><strong> (min-width:992px){.my-lg-0{margin-top:0!important}.my-lg-0{margin-bottom:0!important}}<br>    &lt;/style&gt;</strong><br>    &lt;link <strong>rel=&quot;preload&quot;</strong> href=&quot;<a href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css</a>&quot; integrity=&quot;sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO&quot; crossorigin=&quot;anonymous&quot; <strong>as=&quot;style&quot; onload=&quot;this.onload=null;this.rel=&#39;stylesheet&#39;&quot;</strong>&gt;<br>    <strong>&lt;noscript&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;</strong><a href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"><strong>https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css</strong></a><strong>&quot; integrity=&quot;sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/noscript&gt;</strong><br>    <strong>&lt;script&gt;!function(n){&quot;use strict&quot;;n.loadCSS||(n.loadCSS=function(){});var o=loadCSS.relpreload={};if(o.support=function(){var e;try{e=n.document.createElement(&quot;link&quot;).relList.supports(&quot;preload&quot;)}catch(t){e=!1}return function(){return e}}(),o.bindMediaToggle=function(t){var e=t.media||&quot;all&quot;;function a(){t.media=e}t.addEventListener?t.addEventListener(&quot;load&quot;,a):t.attachEvent&amp;&amp;t.attachEvent(&quot;onload&quot;,a),setTimeout(function(){t.rel=&quot;stylesheet&quot;,t.media=&quot;only x&quot;}),setTimeout(a,3e3)},o.poly=function(){if(!o.support())for(var t=n.document.getElementsByTagName(&quot;link&quot;),e=0;e&lt;t.length;e++){var a=t[e];&quot;preload&quot;!==a.rel||&quot;style&quot;!==a.getAttribute(&quot;as&quot;)||a.getAttribute(&quot;data-loadcss&quot;)||(a.setAttribute(&quot;data-loadcss&quot;,!0),o.bindMediaToggle(a))}},!o.support()){o.poly();var t=n.setInterval(o.poly,500);n.addEventListener?n.addEventListener(&quot;load&quot;,function(){o.poly(),n.clearInterval(t)}):n.attachEvent&amp;&amp;n.attachEvent(&quot;onload&quot;,function(){o.poly(),n.clearInterval(t)})}&quot;undefined&quot;!=typeof exports?exports.loadCSS=loadCSS:n.loadCSS=loadCSS}(&quot;undefined&quot;!=typeof global?global:this);&lt;/script&gt;<br>    &lt;script defer src=&quot;</strong><a href="https://code.jquery.com/jquery-3.3.1.slim.min.js"><strong>https://code.jquery.com/jquery-3.3.1.slim.min.js</strong></a><strong>&quot; integrity=&quot;sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</strong><br>    &lt;script defer src=&quot;<a href="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js">https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js</a>&quot; integrity=&quot;sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;<br>    &lt;script defer src=&quot;<a href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js">https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js</a>&quot; integrity=&quot;sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;<br>    &lt;!--[if lt IE 9]&gt;<br>      &lt;script src=&quot;<a href="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js">https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js</a>&quot;&gt;&lt;/script&gt;<br>    &lt;![endif]--&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;nav class=&quot;navbar navbar-expand-lg navbar-light bg-light&quot; role=&quot;navigation&quot;&gt;<br>      &lt;ul class=&quot;navbar-nav mr-auto&quot;&gt;<br>        &lt;li class=&quot;nav-item active&quot;&gt;<br>          &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;Home&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li class=&quot;nav-item&quot;&gt;<br>          &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;Info&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li class=&quot;nav-item&quot;&gt;<br>          &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;About&lt;/a&gt;<br>        &lt;/li&gt;<br>      &lt;/ul&gt;<br>      &lt;form class=&quot;form-inline my-2 my-lg-0&quot;&gt;<br>        &lt;input class=&quot;form-control mr-sm-2&quot; type=&quot;text&quot; placeholder=&quot;Search&quot; aria-label=&quot;Search&quot;&gt;<br>        &lt;button class=&quot;btn btn-outline-success my-2 my-sm-0&quot; type=&quot;submit&quot;&gt;Search&lt;/button&gt;<br>      &lt;/form&gt;<br>    &lt;/nav&gt;<br>    &lt;main role=&quot;main&quot;&gt;<br>      &lt;section class=&quot;jumbotron&quot;&gt;<br>        &lt;h1 class=&quot;display-4&quot;&gt;Hello there!&lt;/h1&gt;<br>        &lt;p class=&quot;lead&quot;&gt;General info about the page goes here&lt;/p&gt;<br>        &lt;p&gt;&lt;a class=&quot;btn btn-primary btn-lg&quot; href=&quot;#&quot;&gt;Learn more&lt;/a&gt;&lt;/p&gt;<br>      &lt;/section&gt;<br>      &lt;section class=&quot;container&quot;&gt;<br>        &lt;div class=&quot;row&quot;&gt;<br>          &lt;article class=&quot;col-4&quot;&gt;<br>            &lt;h2&gt;First Heading&lt;/h2&gt;<br>            &lt;p&gt;Article content goes here&lt;/p&gt;<br>            &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>          &lt;/article&gt;<br>          &lt;article class=&quot;col-4&quot;&gt;<br>            &lt;h2&gt;Second Heading&lt;/h2&gt;<br>            &lt;p&gt;Article content goes here&lt;/p&gt;<br>            &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>          &lt;/article&gt;<br>          &lt;article class=&quot;col-4&quot;&gt;<br>            &lt;h2&gt;Third Heading&lt;/h2&gt;<br>            &lt;p&gt;Article content goes here&lt;/p&gt;<br>            &lt;ul class=&quot;nav nav-tabs&quot; role=&quot;tablist&quot;&gt;<br>              &lt;li class=&quot;nav-item&quot;&gt;<br>                &lt;a class=&quot;nav-link active&quot; data-toggle=&quot;tab&quot; id=&quot;windows-tab&quot; href=&quot;#windows&quot; role=&quot;tab&quot; aria-controls=&quot;windows&quot; aria-selected=&quot;true&quot;&gt;Windows&lt;/a&gt;<br>              &lt;/li&gt;<br>              &lt;li class=&quot;nav-item&quot;&gt;<br>                &lt;a class=&quot;nav-link&quot; data-toggle=&quot;tab&quot; id=&quot;mac-tab&quot; href=&quot;#mac&quot; role=&quot;tab&quot; aria-controls=&quot;mac&quot; aria-selected=&quot;false&quot;&gt;Mac&lt;/a&gt;<br>              &lt;/li&gt;<br>              &lt;li class=&quot;nav-item&quot;&gt;<br>                &lt;a class=&quot;nav-link&quot; data-toggle=&quot;tab&quot; id=&quot;linux-tab&quot; href=&quot;#linux&quot; role=&quot;tab&quot; aria-controls=&quot;linux&quot; aria-selected=&quot;false&quot;&gt;Linux&lt;/a&gt;<br>              &lt;/li&gt;<br>            &lt;/ul&gt;<br>            &lt;div class=&quot;tab-content&quot;&gt;<br>              &lt;div class=&quot;tab-pane fade show active&quot; id=&quot;windows&quot; role=&quot;tabpanel&quot; aria-labelledby=&quot;windows-tab&quot;&gt;<br>                &lt;img class=&quot;img-fluid&quot; src=&quot;<a href="http://1000logos.net/wp-content/uploads/2017/04/Microsoft-Logo.png">http://1000logos.net/wp-content/uploads/2017/04/Microsoft-Logo.png</a>&quot; alt=&quot;microsoft logo&quot;&gt;<br>                ...<br>              &lt;/div&gt;<br>              &lt;div class=&quot;tab-pane fade&quot; id=&quot;mac&quot; role=&quot;tabpanel&quot; aria-labelledby=&quot;mac-tab&quot;&gt;<br>                &lt;img class=&quot;img-fluid&quot; src=&quot;<a href="https://i.ytimg.com/vi/ipOzBWuYZvg/maxresdefault.jpg">https://i.ytimg.com/vi/ipOzBWuYZvg/maxresdefault.jpg</a>&quot; alt=&quot;apple logo&quot;&gt;<br>                ...<br>              &lt;/div&gt;<br>              &lt;div class=&quot;tab-pane fade&quot; id=&quot;linux&quot; role=&quot;tabpanel&quot; aria-labelledby=&quot;linux-tab&quot;&gt;<br>                &lt;img class=&quot;img-fluid&quot; src=&quot;<a href="https://noware.tech/wp-content/uploads/sites/140/2018/04/linux-1024x565.jpg">https://noware.tech/wp-content/uploads/sites/140/2018/04/linux-1024x565.jpg</a>&quot; alt=&quot;linux logo&quot;&gt;<br>                ...<br>              &lt;/div&gt;<br>            &lt;/div&gt;<br>            &lt;p&gt;&lt;a href=&quot;#&quot;&gt;View details&lt;/a&gt;&lt;/p&gt;<br>          &lt;/article&gt;<br>        &lt;/div&gt;<br>      &lt;/section&gt;<br>    &lt;/main&gt;<br>    &lt;footer class=&quot;navbar navbar-dark bg-secondary&quot; role=&quot;contentinfo&quot;&gt;<br>      &lt;p&gt;Copyright info goes here&lt;/p&gt;<br>    &lt;/footer&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>As you can see, the tool added an inline &lt;style&gt; element with a lot of CSS. Note that this isn’t <strong>all</strong> of Bootstrap’s CSS, just the CSS that the tool analyzed was necessary for the initial view of this page. Bootstrap’s minified CSS alone is 51 KB; this new HTML file, which includes all the HTML plus the newly inlined CSS and JavaScript, is 12 KB. This decreased size is more important than it may seem — getting your initial HTML/CSS/JS under 14 KB can enable your site to render in milliseconds on some of the slowest of connections. This is because each round trip between a browser and a server can send about 14 KB at a time — by fitting everything into a single round trip, you can avoid the overhead of additional round trips (more detailed info <a href="https://developers.google.com/speed/docs/insights/mobile">here</a>).</p><p>The tool also added a rel=&quot;preload&quot; attribute on the CSS link element, which enables <a href="https://www.filamentgroup.com/lab/async-css.html">the CSS file to be loaded asynchronously</a>. This is normally something you don’t want to do — although it would speed up the site, the user would see the unstyled plain HTML first, then see the properly styled site a few moments later when the CSS file is loaded. However, in our case, since we’re inlining the critical CSS, this isn’t an issue, so loading the remaining CSS asynchronously works perfectly!</p><h4>Implementing a build step</h4><p>At this point you could do things like manually minify and concatenate your files each time you deploy your website, but it would be a huge pain. Ideally you would automate this set of tasks with a single command, which is known as a <strong>build step</strong>. Minification and concatenation are just two of the possible tasks — there can be tasks for anything repetitive that can be automated. Here are some typical tasks in a build step:</p><ul><li>Minify the HTML, CSS, and JavaScript</li><li>Concatenate JavaScript files and CSS files</li><li>Inline critical CSS</li><li>Optimize images (by resizing, removing unused metadata, etc.)</li><li>Add CSS vendor prefixes for browser compatibility</li><li>Transpiling code (from SASS to CSS, or CoffeeScript to JS, etc.)</li><li>Run code testing</li></ul><p>To implement a build step, you’ll need to pick a tool, and there are many to choose from. One popular choice was <a href="https://gruntjs.com/">Grunt</a>, which was released in 2012. It was followed by <a href="https://gulpjs.com/">Gulp</a>, as well as <a href="http://broccolijs.com/">Broccoli.js</a>, <a href="https://brunch.io/">Brunch</a>, and <a href="https://webpack.github.io/">webpack</a>. As of 2018, webpack seems to be the most popular choice, but ultimately any and every one of these tools will serve the purpose of implementing a build step well.</p><blockquote><strong>Note</strong>: Using tools for a build step can be quite daunting to learn from scratch. Most tools require you to use the command line — if you’ve never used one before, you can read <a href="https://www.learnenough.com/command-line-tutorial">this tutorial</a> to get a good overview to get started. Many of the popular web developer build tools in 2018 are based on node.js — if you’re unfamiliar with the node.js ecosystem and its use in frontend development, you can read my article <a href="https://medium.com/the-node-js-collection/modern-javascript-explained-for-dinosaurs-f695e9747b70">Modern JavaScript Explained For Dinosaurs</a> for an overview of that as well.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GbAw9ahi_dxD6LBA6Kh0-g.png" /></figure><h3>Improving maintainability with templates and components</h3><p>So far we have a decent web page, both reasonably attractive and performant. Right now it looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fO1wXfiWfdv8vY3_qeQv0A.png" /></figure><p>In the navbar, there’s a link to an About page, but it currently doesn’t go anywhere. What if we wanted to make this About page? The most straightforward answer would be to make a copy of the index.html called about.html, and update the content accordingly. Specifically, the content in the &lt;main&gt; element would change, and the rest of the HTML would remain unchanged. Here’s what a simple about.html page would look like:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;utf-8&quot;&gt;<br>    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;<br>    &lt;meta name=&quot;description&quot; content=&quot;&quot;&gt;<br>    &lt;meta name=&quot;author&quot; content=&quot;&quot;&gt;<br>    &lt;title&gt;My test page&lt;/title&gt;<br>    &lt;link rel=&quot;stylesheet&quot; href=&quot;<a href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css</a>&quot; integrity=&quot;sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO&quot; crossorigin=&quot;anonymous&quot;&gt;<br>    &lt;script defer src=&quot;<a href="https://code.jquery.com/jquery-3.3.1.slim.min.js">https://code.jquery.com/jquery-3.3.1.slim.min.js</a>&quot; integrity=&quot;sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;<br>    &lt;script defer src=&quot;<a href="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js">https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js</a>&quot; integrity=&quot;sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;<br>    &lt;script defer src=&quot;<a href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js">https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js</a>&quot; integrity=&quot;sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;<br>    &lt;!--[if lt IE 9]&gt;<br>      &lt;script src=&quot;<a href="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js">https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js</a>&quot;&gt;&lt;/script&gt;<br>    &lt;![endif]--&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;nav class=&quot;navbar navbar-expand-lg navbar-light bg-light&quot; role=&quot;navigation&quot;&gt;<br>      &lt;ul class=&quot;navbar-nav mr-auto&quot;&gt;<br>        &lt;li class=&quot;nav-item active&quot;&gt;<br>          &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;Home&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li class=&quot;nav-item&quot;&gt;<br>          &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;Info&lt;/a&gt;<br>        &lt;/li&gt;<br>        &lt;li class=&quot;nav-item&quot;&gt;<br>          &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;About&lt;/a&gt;<br>        &lt;/li&gt;<br>      &lt;/ul&gt;<br>      &lt;form class=&quot;form-inline my-2 my-lg-0&quot;&gt;<br>        &lt;input class=&quot;form-control mr-sm-2&quot; type=&quot;text&quot; placeholder=&quot;Search&quot; aria-label=&quot;Search&quot;&gt;<br>        &lt;button class=&quot;btn btn-outline-success my-2 my-sm-0&quot; type=&quot;submit&quot;&gt;Search&lt;/button&gt;<br>      &lt;/form&gt;<br>    &lt;/nav&gt;<br>    &lt;main role=&quot;main&quot;&gt;<br><strong>      &lt;h1&gt;About&lt;/h1&gt;<br>      &lt;p&gt;Info about this site&lt;/p&gt;</strong><br>    &lt;/main&gt;<br>    &lt;footer class=&quot;navbar navbar-dark bg-secondary&quot; role=&quot;contentinfo&quot;&gt;<br>      &lt;p&gt;Copyright info goes here&lt;/p&gt;<br>    &lt;/footer&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>Note that everything in about.html is identical to index.html, except for the content in bold. While this works, it becomes problematic from a maintenance perspective. If we make 7 different pages using this technique, it would be a lot of duplicated code. If we later want to make a change to the navbar, we would have to copy the changes over into all 7 files. This violates the well-known software principle called <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> (Don’t Repeat Yourself).</p><h4>Building templates on the server</h4><p>One solution to this problem is to use a templating engine. This involves writing non-standard HTML in your HTML, which would then be fed into a separate program that would replace the non-standard HTML with standard HTML. This is best explained with an example.</p><p>Say you’re using PHP, one of the earliest languages designed to work with HTML (and still being used by many large companies today). You would make a head.php file (which contains the content from the &lt;head&gt; element), header.php file (which contains the navbar elements), and footer.php (which contains the footer elements).</p><p>Once you have these files, you can make an index.php file which looks like this:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    <strong>&lt;?php include(&quot;head.php&quot;);?&gt;</strong><br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    <strong>&lt;?php include(&quot;header.php&quot;);?&gt;</strong><br>    &lt;main role=&quot;main&quot;&gt;<br>      &lt;section class=&quot;jumbotron&quot;&gt;<br>        ...<br>      &lt;/section&gt;<br>      &lt;section class=&quot;container&quot;&gt;<br>        ...<br>      &lt;/section&gt;<br>    &lt;/main&gt;<br>    <strong>&lt;?php include(&quot;footer.php&quot;);?&gt;</strong><br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>And the about.php file would look like this:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    <strong>&lt;?php include(&quot;head.php&quot;);?&gt;</strong><br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    <strong>&lt;?php include(&quot;header.php&quot;);?&gt;</strong><br>    &lt;main role=&quot;main&quot;&gt;<br>      &lt;h1&gt;About&lt;/h1&gt;<br>      &lt;p&gt;Info about this site&lt;/p&gt;<br>    &lt;/main&gt;<br>    <strong>&lt;?php include(&quot;footer.php&quot;);?&gt;</strong><br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>So here you can see that the only part that changes is the content in the middle. If you need to update the header, footer, or external dependencies, you would only need to change them a single time.</p><p>The above code is obviously not valid HTML — you need some sort of build step to replace the include statements with the HTML from the separate files. We actually could incorporate it into the build step that we previously saw (for code minification, file concatenation, critical CSS, etc.). However, this step of generating HTML from templates has traditionally been done dynamically on the server<strong>.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3fBZ27QgR7eDCoPTjam6nw.png" /><figcaption>From <a href="https://en.wikipedia.org/wiki/Client%E2%80%93server_model">wikipedia.org</a></figcaption></figure><p>A <strong>server</strong> is the computer that receives web requests and sends back HTML/CSS/JS as a web response (as opposed to the <strong>client, </strong>the computer with the web browser that initiates the web request). A server is generally responsible for creating dynamic HTML based on data in a database. For example, if you search for “red bananas” on <a href="http://www.google.com">www.google.com</a>, there isn’t some unique HTML file just about red bananas that gets sent to you from a server. Instead, the server runs code to dynamically create an HTML response based on your search terms. So here you can kill two birds with one stone — since you’re already have a step to generate dynamic HTML on the server, you can use templates to define the generated HTML to keep your code DRY.</p><p>Building HTML with templates on the server is a solution that has been the de facto standard for quite some time. Besides PHP, there’s <a href="https://en.wikipedia.org/wiki/ERuby">ERB</a> for the <a href="https://rubyonrails.org/">Ruby on Rails</a> framework, the <a href="https://docs.djangoproject.com/en/1.7/topics/templates/">Django template language</a> for Python’s <a href="https://www.djangoproject.com/">Django</a> framework, <a href="http://ejs.co/">EJS</a> for Node’s <a href="https://expressjs.com/">Express</a> framework, etc. This approach can be quite intimidating — in order to take advantage of a template engine to write maintainable code, you essentially first have to learn an entire programming language and web framework! If you were already planning on working with servers and databases, this is a natural fit. But if you were just interested in writing HTML on the frontend, this is quite honestly a huge barrier to entry.</p><blockquote>Note: If your website doesn’t require a database, you could instead use a <strong>static site generator</strong>, which uses templates to build static HTML files (<a href="https://jekyllrb.com/">Jekyll</a>, <a href="https://gohugo.io/">Hugo</a>, and <a href="https://www.gatsbyjs.org/">Gatsby</a> are some popular choices). Static site generators can be simpler to use compared to a server-side web framework; however, you’re still required to learn a separate programming language or environment, so there’s still a barrier to entry compared to writing plain HTML.</blockquote><h4>Using web components on the client</h4><p>Web components were first introduced in 2011 as a completely different approach to solving the maintainability issues of HTML. Web components are built on the <strong>client</strong> as opposed to the <strong>server</strong>, which removes the barrier of having to learn a server-side programming language and web framework just to write maintainable HTML.</p><p>The overall goal of web components is to be able to create reusable widgets. Looking back at the previous example, you could create a navbar component, a header component, and a footer component. Going further, you could create a jumbotron component and an articles component for the content within the page. Then you could use the components in index.html as follows:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>   <strong> </strong>...<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    <strong>&lt;navbar-component&gt;&lt;/navbar-component&gt;</strong><br>    &lt;main role=&quot;main&quot;&gt;<br>      <strong>&lt;jumbotron-component&gt;&lt;/jumbotron-component&gt;</strong><br>      &lt;section class=&quot;container&quot;&gt;<br>        <strong>&lt;articles-component&gt;&lt;/articles-component&gt;</strong><br>      &lt;/section&gt;<br>    &lt;/main&gt;<br>    <strong>&lt;footer-component&gt;&lt;/footer-component&gt;</strong><br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>In order for this to work, we need to create <strong>custom elements</strong>, where we essentially are defining new elements for the HTML language (that will only work for this particular website). To create a custom element, you have to use JavaScript. Here’s an example of the JavaScript necessary to create the navbar custom element:</p><pre>window.customElements.define(<br>  &quot;navbar-component&quot;,<br>  class extends HTMLElement {<br>    connectedCallback() {<br>      this.innerHTML = `<br><strong>        &lt;nav class=&quot;navbar navbar-expand-lg navbar-light bg-light&quot; role=&quot;navigation&quot;&gt;<br>          &lt;ul class=&quot;navbar-nav mr-auto&quot;&gt;<br>            &lt;li class=&quot;nav-item active&quot;&gt;<br>              &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;Home&lt;/a&gt;<br>            &lt;/li&gt;<br>            &lt;li class=&quot;nav-item&quot;&gt;<br>              &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;Info&lt;/a&gt;<br>            &lt;/li&gt;<br>            &lt;li class=&quot;nav-item&quot;&gt;<br>              &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;About&lt;/a&gt;<br>            &lt;/li&gt;<br>          &lt;/ul&gt;<br>          &lt;form class=&quot;form-inline my-2 my-lg-0&quot;&gt;<br>            &lt;input class=&quot;form-control mr-sm-2&quot; type=&quot;text&quot; placeholder=&quot;Search&quot; aria-label=&quot;Search&quot;&gt;<br>            &lt;button class=&quot;btn btn-outline-success my-2 my-sm-0&quot; type=&quot;submit&quot;&gt;Search&lt;/button&gt;<br>          &lt;/form&gt;<br>        &lt;/nav&gt;</strong><br>       `;<br>    }<br>  }<br>);</pre><p>The JavaScript looks a bit complex up front (it uses new <a href="https://en.wikipedia.org/wiki/ECMAScript#6th_Edition_-_ECMAScript_2015">ES2015</a> language features), but all it’s doing in this example is defining the HTML for the navbar (shown above in bold) and registering the name navbar-component as a custom element. Note that the HTML is being defined in the JavaScript file using a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">template literal</a>, essentially a giant string — if you prefer to move this back into a regular HTML file, we’ll need a different mechanism which we’ll discuss in a bit. If you add this JavaScript to the page, you now have the ability to make &lt;navbar-component&gt; elements in addition to normal HTML elements.</p><p>So far in this example, there isn’t much advantage to this approach over the server side template approach. However, the benefits become more clear when you start adding JavaScript functionality and CSS styling to each component. Web components offer features to isolate functionality and styling to stay within each component, making them reusable — not just for this site, but theoretically reusable across multiple projects. This concept can be seen with existing HTML elements such as the &lt;video&gt; element. If you write HTML like this:</p><pre>&lt;video width=&quot;320&quot; height=&quot;240&quot; controls loop muted&gt;<br>  &lt;source src=&quot;movie.mp4&quot; type=&quot;video/mp4&quot;&gt;<br>  &lt;source src=&quot;movie.ogg&quot; type=&quot;video/ogg&quot;&gt;<br>  &lt;p&gt;Your browser doesn&#39;t support HTML5 video. Here is<br>     a &lt;a href=&quot;movie.mp4&quot;&gt;link to the video&lt;/a&gt; instead.&lt;/p&gt;<br>&lt;/video&gt;</pre><p>You would get a video player that looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3pRSQmwBda1IeFo1Smrg_A.png" /><figcaption>(this is a picture of a video, not an actual video)</figcaption></figure><p>The &lt;video&gt; element comes with its own JavaScript interactive controls and CSS styling which are isolated from the rest of the page. This means when you use a &lt;video&gt; element, you don’t have to worry about it affecting the styles or functionality of your site, and you also don’t have to worry about any of the CSS or JavaScript from the site breaking the video component.</p><p>The goal of web components is to give developers the ability to create their own custom components similar to the &lt;video&gt; element, with all of its isolation and reusability benefits. Here’s how all the parts of the web components specification work together:</p><ul><li>You can create <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements">custom elements</a> in JavaScript as shown earlier. You then can define custom functionality in your JavaScript, as well as pass in attributes from the HTML into the custom element.</li><li>You can use the <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM">shadow DOM</a> to style the custom element with CSS that only applies to the element as opposed to the entire document (solving one of the most difficult aspects of CSS).</li><li>If you don’t want to write all your HTML directly in JavaScript, you can use <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template">HTML templates</a>, where you write the component’s HTML in a normal HTML file in a &lt;template&gt; tag, which won’t get rendered until it gets called by your JavaScript.</li><li>To organize your code, you can use <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/HTML_Imports">HTML imports</a>, where you can put all the HTML, CSS, and JavaScript necessary to define a component in a file called navbar-component.html, which you then would import in the main HTML file just like you would an external CSS file: <br>&lt;link rel=&quot;import&quot; href=&quot;navbar-component.html&quot;&gt;.</li></ul><p>When web components were first announced in 2011, many developers were excited by the possibilities. While server side templating approaches help solve some of the maintainability issues of HTML, web components offered something completely different — the promise of extending HTML will fully featured reusable widgets. This was the missing piece to make the web platform one where you can develop complex applications, as opposed to the simple static content sites it was originally designed for.</p><p>So what happened? Over the next several years, it became clear that browsers were not in agreement about web components as a standard. As of 2018, almost no browser fully supports the four main features of web components as described above, due to potential issues with implementation performance, conflicting standards, and varying corporate interests. This left developers in an interesting position — almost everyone agreed that a component style approach was a necessary part of moving the web from static sites to complex applications, but waiting for browser support seemed like an exercise in futility. What to do?</p><h4>Using JavaScript frameworks on the client</h4><p>By the time it became clear that the HTML web component specification wasn’t going to be implemented anytime soon, JavaScript was already a powerful enough language to pick up the slack. Developers had been making complex applications using the <a href="https://jquery.com/">jQuery</a> library (released in 2006), although it was difficult to organize the code for large scale applications. <a href="http://backbonejs.org/">Backbone.js</a> (released in 2010) was one of the first popular libraries designed to provide a framework to organize code for large single-page applications, followed by <a href="https://angularjs.org/">AngularJS</a>, <a href="https://www.emberjs.com/">Ember.js</a>, and many others.</p><p>All these frameworks worked with existing JavaScript features — they didn’t have to rely on waiting for browsers to implement the web component specification. But none of the frameworks used true components that were isolated and reusable; without the 4 parts of the web component specification (custom elements, shadow DOM, HTML templates, and HTML imports), it didn’t seem possible.</p><p>In 2013, a framework called <a href="https://reactjs.org/">React</a> was released that had an interesting take on this situation. They were able to make a true component-based framework without the web component specification, using the following approaches:</p><ul><li>Instead of using web component’s custom elements specification, React took the approach of defining <strong>all</strong> HTML in JavaScript. Essentially you would define JavaScript functions to output the desired HTML using a special syntax called JSX (which looks like HTML, but gets converted into JavaScript functions using a build step).</li><li>Instead of using web component’s HTML template specification, React provided no way to write HTML outside of JavaScript.</li><li>Instead of using web component’s HTML import specification, React took the approach of importing JavaScript into JavaScript. This wasn’t actually directly possible at the time, but tools such as <a href="http://browserify.org/">Browserify</a> and <a href="https://webpack.js.org/">webpack</a> allowed developers to write require or import statements in their JavaScript which would get converted at build time into a single JavaScript bundle.</li></ul><p>Essentially the insight here is that you could make components work by doing <strong>everything</strong> in JavaScript. Note that one part of the web component specification is missing here, the shadow DOM — React didn’t have a solution to isolated styling when it was first released. Nevertheless, it was enough to provide a framework for building applications with components today.</p><p>This is what the JavaScript looks like using React to make a navbar component:</p><pre>import React, { Component } from &#39;react&#39;;</pre><pre>class Navbar extends Component {<br>  render() {<br>    return (<br>      &lt;nav className=&quot;navbar navbar-expand-lg navbar-light bg-light&quot; role=&quot;navigation&quot;&gt;<br>        &lt;ul className=&quot;navbar-nav mr-auto&quot;&gt;<br>          &lt;li className=&quot;nav-item active&quot;&gt;<br>            &lt;a className=&quot;nav-link&quot; href=&quot;#&quot;&gt;Home&lt;/a&gt;<br>          &lt;/li&gt;<br>          &lt;li className=&quot;nav-item&quot;&gt;<br>            &lt;a className=&quot;nav-link&quot; href=&quot;#&quot;&gt;Info&lt;/a&gt;<br>          &lt;/li&gt;<br>          &lt;li className=&quot;nav-item&quot;&gt;<br>            &lt;a className=&quot;nav-link&quot; href=&quot;#&quot;&gt;About&lt;/a&gt;<br>          &lt;/li&gt;<br>        &lt;/ul&gt;<br>        &lt;form className=&quot;form-inline my-2 my-lg-0&quot;&gt;<br>          &lt;input className=&quot;form-control mr-sm-2&quot; type=&quot;text&quot; placeholder=&quot;Search&quot; aria-label=&quot;Search&quot;/&gt;<br>          &lt;button className=&quot;btn btn-outline-success my-2 my-sm-0&quot; type=&quot;submit&quot;&gt;Search&lt;/button&gt;<br>        &lt;/form&gt;<br>      &lt;/nav&gt;<br>    );<br>  }<br>}</pre><pre>export default Navbar;</pre><p>This isn’t too different from the custom element example shown earlier (although the JSX syntax is unsettling to most developers when they first see it). The idea in React is to break this into further sub-components, such that each component does only one thing.</p><p>When React was first released, it was met with a lot of criticism, particularly for its seeming lack of separation of concerns (where developers were taught to keep HTML, CSS, and JS completely separate for maintainability). However, React pushed forward the idea that for complex web applications, separation of concerns isn’t about boundaries between technologies (HTML, CSS, and JS), but instead is about boundaries between units of functionality (in other words, components).</p><p>In addition to being a component-based framework, React brought with it strong opinions about how to manage data within an application, using a declarative approach. What this means is that with React, you don’t need to write code to update the interface directly. Instead you define what the interface is supposed to look like (using JSX), write code to update the data, then let React figure out how to update and render the interface efficiently with its <a href="https://reactjs.org/docs/faq-internals.html">virtual DOM</a> implementation (not to be confused with the shadow DOM). This was a major shift in web framework design, influential to the point where every major framework going forward openly borrowed React’s declarative approach using a virtual DOM implementation — Ember, Angular, Vue.js, the list goes on. As of 2018, the web development community has largely embraced this paradigm as the way of building modern web applications.</p><p>Note that the desire to write HTML in a clear and maintainable way has led us to a place which requires a great deal of programming knowledge; it is almost impossible to avoid JavaScript in particular. In some sense this breaks the promise of HTML, which was designed to be a language that didn’t require an understanding of programming to be used effectively. It’s possible there will come a future where developers can share pre-built web components in pure HTML, but it may take quite some time before that future arrives.</p><blockquote>This section provides only a brief overview of the frontend approaches taken by React and other similar frameworks. If you want more complete explanations and tutorials on how to build a working application using various JavaScript frameworks and approaches, check out my series <a href="https://medium.com/actualize-network/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">Comparing Frontend Approaches: A look at jQuery, Vue.js, React, and Elm</a>.</blockquote><h3>Conclusion</h3><p>So this is modern HTML in a nutshell. We covered <strong>writing semantic and accessible content</strong> using appropriate tags and aria attributes, using <strong>CSS and JavaScript</strong> to add styling and dynamic features, using <strong>HTML attributes and tooling</strong> to improve performance, and finally <strong>using templates and components</strong> to improve maintainability. Along the way we can see that to take full advantage of modern HTML, it is nearly impossible to avoid using a build process as well as some form of separate programming language, which is often JavaScript for most approaches.</p><p>Looking at it from a high level can sometimes be discouraging — what used to be a simple and accessible undertaking (making a website with HTML) has now become complex and seemingly unapproachable (making a web application with a JavaScript frontend framework, using a build process with thousands of potentially fragile dependencies). Yet it’s important to note that web development as an industry has only existed for around 30 years — this is a tiny fraction of history compared to other industries (such as architecture, which has been around for many centuries). It’s as if web developers just learned how to build houses out of clay, and are now being asked to use the same tools to build skyscrapers. It’s only natural that our tools and processes evolve; we just have to make sure it evolves in a way that’s inclusive to the original vision of <a href="https://www.vanityfair.com/news/2018/07/the-man-who-created-the-world-wide-web-has-some-regrets">the web as a democratic platform</a>.</p><p>Modern HTML can definitely be frustrating to work with as it continues to change and evolve at a rapid pace. Yet we’re able to do more now than ever before, and we are all essentially at the ground floor of a new industry with the potential to shape it into the platform we want it to become. It’s an exciting time to be a developer, and I hope this information can serve as a roadmap to help you on your journey!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fHND-kRQTWkLMFlOwbZNrw.png" /></figure><p><em>Special thanks to </em><a href="https://twitter.com/ryanqnorth"><em>@ryanqnorth</em></a><em>’s </em><a href="http://www.qwantz.com/"><em>Dinosaur Comics</em></a><em>, which has served up some of the finest absurdist humor since 2003 (when dinosaurs ruled the web).</em></p><blockquote>Interested in this style of learning? Be sure to check out <a href="http://anyonecanlearntocode.com/">Actualize coding bootcamp</a> (where I am the Dean of Instruction as well as a lead instructor). We offer <a href="http://anyonecanlearntocode.com/chicago-coding-bootcamp">in-person classes</a> in Chicago as well as <a href="http://anyonecanlearntocode.com/online-coding-bootcamp">live online classes</a> to help people transition into new careers as modern web developers!</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=65e56af2981" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/modern-html-explained-for-dinosaurs-65e56af2981">Modern HTML Explained For Dinosaurs</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Modern CSS Explained For Dinosaurs]]></title>
            <link>https://medium.com/actualize-network/modern-css-explained-for-dinosaurs-5226febe3525?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/5226febe3525</guid>
            <category><![CDATA[css-layout]]></category>
            <category><![CDATA[css]]></category>
            <category><![CDATA[css-preprocessors]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Sun, 04 Feb 2018 19:03:36 GMT</pubDate>
            <atom:updated>2020-09-27T01:24:31.437Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kXsiGLMHSS4jUx4W6yBOlw.png" /><figcaption>Images from <a href="http://www.qwantz.com/">Dinosaur Comics</a> by <a href="https://twitter.com/ryanqnorth">Ryan North</a></figcaption></figure><p>CSS is strangely considered both one of the easiest and one of the hardest languages to learn as a web developer. It’s certainly easy enough to get started with it — you define style properties and values to apply to specific elements, and…that’s pretty much all you need to get going! However, it gets tangled and complicated to organize CSS in a meaningful way for larger projects. Changing any line of CSS to style an element on one page often leads to unintended changes for elements on other pages.</p><p>In order to deal with the inherent complexity of CSS, all sorts of different best practices have been established. The problem is that there isn’t any strong consensus on which best practices are in fact the best, and many of them seem to completely contradict each other. If you’re trying to learn CSS for the first time, this can be disorienting to say the least.</p><p>The goal of this article is to provide a historical context of how CSS approaches and tooling have evolved to what they are today in 2018. By understanding this history, it will be easier to understand each approach and how to use them to your benefit. Let’s get started!</p><blockquote><strong>Update</strong>: I made a new video course version of this article, which goes over the material with greater depth, check it out here: <br><a href="https://firstclass.actualize.co/p/modern-css-explained-for-dinosaurs">https://firstclass.actualize.co/p/modern-css-explained-for-dinosaurs</a></blockquote><h3>Using CSS for basic styling</h3><p>Let’s start with a basic website using just a simple <strong>index.html </strong>file that links to a separate <strong>index.css</strong> file:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>&lt;head&gt;<br>  &lt;meta charset=&quot;UTF-8&quot;&gt;<br>  &lt;title&gt;Modern CSS&lt;/title&gt;<br>  &lt;link rel=&quot;stylesheet&quot; href=&quot;index.css&quot;&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br>  &lt;header&gt;This is the header.&lt;/header&gt;<br>  &lt;main&gt;<br>    &lt;h1&gt;This is the main content.&lt;/h1&gt;<br>    &lt;p&gt;...&lt;/p&gt;<br>  &lt;/main&gt;<br>  &lt;nav&gt;<br>    &lt;h4&gt;This is the navigation section.&lt;/h4&gt;<br>    &lt;p&gt;...&lt;/p&gt;<br>  &lt;/nav&gt;<br>  &lt;aside&gt;<br>    &lt;h4&gt;This is an aside section.&lt;/h4&gt;<br>    &lt;p&gt;...&lt;/p&gt;<br>  &lt;/aside&gt;<br>  &lt;footer&gt;This is the footer.&lt;/footer&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</pre><p>Right now we aren’t using any classes or ids in the HTML, just <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element#Content_sectioning">semantic tags</a>. Without any CSS, the website looks like this (using placeholder text):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MwOy9E5LacvhGEtIUb9pXQ.png" /><figcaption>Click <a href="https://codepen.io/peterxjang/pen/qxbxwK?editors=1100">here</a> to see a live example</figcaption></figure><p>Functional, but not very pretty. We can add CSS to improve the basic typography in <strong>index.css</strong>:</p><pre>/* BASIC TYPOGRAPHY                       */<br>/* from <a href="https://github.com/oxalorg/sakura">https://github.com/oxalorg/sakura</a> */</pre><pre>html {<br>  font-size: 62.5%;<br>  font-family: serif;<br>}</pre><pre>body {<br>  font-size: 1.8rem;<br>  line-height: 1.618;<br>  max-width: 38em;<br>  margin: auto;<br>  color: #4a4a4a;<br>  background-color: #f9f9f9;<br>  padding: 13px;<br>}</pre><pre>@media (max-width: 684px) {<br>  body {<br>    font-size: 1.53rem;<br>  }<br>}</pre><pre>@media (max-width: 382px) {<br>  body {<br>    font-size: 1.35rem;<br>  }<br>}</pre><pre>h1, h2, h3, h4, h5, h6 {<br>  line-height: 1.1;<br>  font-family: Verdana, Geneva, sans-serif;<br>  font-weight: 700;<br>  overflow-wrap: break-word;<br>  word-wrap: break-word;<br>  -ms-word-break: break-all;<br>  word-break: break-word;<br>  -ms-hyphens: auto;<br>  -moz-hyphens: auto;<br>  -webkit-hyphens: auto;<br>  hyphens: auto;<br>}</pre><pre>h1 {<br>  font-size: 2.35em;<br>}</pre><pre>h2 {<br>  font-size: 2em;<br>}</pre><pre>h3 {<br>  font-size: 1.75em;<br>}</pre><pre>h4 {<br>  font-size: 1.5em;<br>}</pre><pre>h5 {<br>  font-size: 1.25em;<br>}</pre><pre>h6 {<br>  font-size: 1em;<br>}</pre><p>Here most of the CSS is styling the typography (fonts with sizes, line height, etc.), with some styling for the colors and a centered layout. You’d have to study design to know good values to choose for each of these properties (these styles are from <a href="https://github.com/oxalorg/sakura">sakura.css</a>), but the CSS itself that’s being applied here isn’t too complicated to read. The result looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_rykiEopx6MCsnOAAcT8MA.png" /><figcaption>Click <a href="https://codepen.io/peterxjang/pen/oEbERP?editors=1100">here</a> to see a live example</figcaption></figure><p>What a difference! This is the promise of CSS — a simple way to add styles to a document, without requiring programming or complex logic. Unfortunately, things start to get hairier when we use CSS for more than just typography and colors (which we’ll tackle next).</p><h3>Using CSS for layout</h3><p>In the 1990s, before CSS gained wide adoption, there weren’t a lot of options to layout content on the page. HTML was originally designed as a language to create plain documents, not dynamic websites with sidebars, columns, etc. In those early days, layout was often done using HTML tables — the entire webpage would be within a table, which could be used to organize the content in rows and columns. This approach worked, but the downside was the tight coupling of content and presentation — if you wanted to change the layout of a site, it would require rewriting significant amounts of HTML.</p><p>Once CSS entered the scene, there was a strong push to keep content (written in the HTML) separate from presentation (written in the CSS). So people found ways to move all layout code out of HTML (no more tables) into CSS. It’s important to note that like HTML, CSS wasn’t really designed to layout content on a page either, so early attempts at this separation of concerns were difficult to achieve gracefully.</p><p>Let’s take a look at how this works in practice with our above example. Before we define any CSS layout, we’ll first reset any margins and paddings (which affect layout calculations) as well as give section distinct colors (not to make it pretty, but to make each section visually stand out when testing different layouts).</p><pre>/* RESET LAYOUT AND ADD COLORS */</pre><pre>body {<br>  margin: 0;<br>  padding: 0;<br>  max-width: inherit;<br>  background: #fff;<br>  color: #4a4a4a;<br>}</pre><pre>header, footer {<br>  font-size: large;<br>  text-align: center;<br>  padding: 0.3em 0;<br>  background-color: #4a4a4a;<br>  color: #f9f9f9;<br>}</pre><pre>nav {<br>  background: #eee;<br>}</pre><pre>main {<br>  background: #f9f9f9;<br>}</pre><pre>aside {<br>  background: #eee;<br>}</pre><p>Now the website temporarily looks like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ii8mtqbLaS5GIB3VJTpqSw.png" /><figcaption>Click <a href="https://codepen.io/peterxjang/pen/jZWzEV?editors=1100">here</a> to see a live example</figcaption></figure><p>Now we’re ready to use CSS to layout the content on the page. We’ll look at three different approaches in chronological order, starting with the classic float-based layouts.</p><h4>Float-based layout</h4><p>The CSS float property was originally introduced to float an image inside a column of text on the left or right (something you often see in newspapers). Web developers in the early 2000s took advantage of the fact that you could float not just images, but any element, meaning you could create the illusion of rows and columns by floating entire divs of content. But again, floats weren’t designed for this purpose, so creating this illusion was difficult to pull off in a consistent fashion.</p><p>In 2006, <a href="https://alistapart.com/">A List Apart</a> published the popular article <a href="https://alistapart.com/article/holygrail">In Search of the Holy Grail</a>, which outlined a detailed and thorough approach to creating what was known as the Holy Grail layout — a header, three columns and a footer. It’s pretty crazy to think that what sounds like a fairly straightforward layout would be referred to as the Holy Grail, but that was indeed how hard it was to create consistent layout at the time using pure CSS.</p><p>Below is a float-based layout for our example based on the technique described in that article:</p><pre>/* FLOAT-BASED LAYOUT */</pre><pre>body {<br>  padding-left: 200px;<br>  padding-right: 190px;<br>  min-width: 240px;<br>}</pre><pre>header, footer {<br>  margin-left: -200px;<br>  margin-right: -190px;   <br>}</pre><pre>main, nav, aside {<br>  position: relative;<br>  float: left;<br>}</pre><pre>main {<br>  padding: 0 20px;<br>  width: 100%;<br>}</pre><pre>nav {<br>  width: 180px;<br>  padding: 0 10px;<br>  right: 240px;<br>  margin-left: -100%;<br>}</pre><pre>aside {<br>  width: 130px;<br>  padding: 0 10px;<br>  margin-right: -100%;<br>}</pre><pre>footer {<br>  clear: both;<br>}</pre><pre>* html nav {<br>  left: 150px;<br>}</pre><p>Looking at the CSS, you can see there are quite a few hacks necessary to get it to work (negative margins, the clear: both property, hard-coded width calculations, etc.) — the <a href="https://alistapart.com/article/holygrail">article</a> does a good job explaining the reasoning for each in detail. Below is what the result looks like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_2LrWDjxL8Q33fL6Ci4hIw.png" /><figcaption>Click <a href="https://codepen.io/peterxjang/pen/VQeXYg?editors=1100">here</a> to see a live example</figcaption></figure><p>This is nice, but you can see from the colors that the three columns are not equal in height, and the page doesn’t fill the height of the screen. These issues are inherent with a float-based approach. All a float can do is place content to the left or right of a section — the CSS has no way to infer the heights of the content in the other sections. This problem had no straightforward solution until many years later, with a flexbox-based layout.</p><h4>Flexbox-based layout</h4><p>The flexbox CSS property was first proposed in 2009, but didn’t get widespread browser adoption until around 2015. Flexbox was designed to define how space is distributed across a single column or row, which makes it a better candidate for defining layout compared to using floats. This meant that after about a decade of using float-based layouts, web developers were finally able to use CSS for layout without the need for the hacks needed with floats.</p><p>Below is a flexbox-based layout for our example based on the technique described on the site <a href="https://philipwalton.github.io/solved-by-flexbox/demos/holy-grail/">Solved by Flexbox</a> (a popular resource showcasing different flexbox examples). Note that in order to make flexbox work, we need to an an extra wrapper div around the three columns in the HTML:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>&lt;head&gt;<br>  &lt;meta charset=&quot;UTF-8&quot;&gt;<br>  &lt;title&gt;Modern CSS&lt;/title&gt;<br>  &lt;link rel=&quot;stylesheet&quot; href=&quot;index.css&quot;&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br>  &lt;header&gt;This is the header.&lt;/header&gt;<br>  <strong>&lt;div class=&quot;container&quot;&gt;</strong><br>    &lt;main&gt;<br>      &lt;h1&gt;This is the main content.&lt;/h1&gt;<br>      &lt;p&gt;...&lt;/p&gt;<br>    &lt;/main&gt;<br>    &lt;nav&gt;<br>      &lt;h4&gt;This is the navigation section.&lt;/h4&gt;<br>      &lt;p&gt;...&lt;/p&gt;<br>    &lt;/nav&gt;<br>    &lt;aside&gt;<br>      &lt;h4&gt;This is an aside section.&lt;/h4&gt;<br>      &lt;p&gt;...&lt;/p&gt;<br>    &lt;/aside&gt;<br>  <strong>&lt;/div&gt;</strong><br>  &lt;footer&gt;This is the footer.&lt;/footer&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</pre><p>And here’s the flexbox code in the CSS:</p><pre>/* FLEXBOX-BASED LAYOUT */</pre><pre>body {<br>  min-height: 100vh;<br>  display: flex;<br>  flex-direction: column;<br>}</pre><pre>.container {<br>  display: flex;<br>  flex: 1;<br>}</pre><pre>main {<br>  flex: 1;<br>  padding: 0 20px;<br>}</pre><pre>nav {<br>  flex: 0 0 180px;<br>  padding: 0 10px;<br>  order: -1;<br>}</pre><pre>aside {<br>  flex: 0 0 130px;<br>  padding: 0 10px;<br>}</pre><p>That is way, way more compact compared to the float-based layout approach! The flexbox properties and values are a bit confusing at first glance, but it eliminates the need for a lot of the hacks like negative margins that were necessary with float-based layouts — a huge win. Here is what the result looks like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*I6qa6O31X-jlilGCrcSTsQ.png" /><figcaption>Click <a href="https://codepen.io/peterxjang/pen/xYZWGz?editors=1100">here</a> for a live example</figcaption></figure><p>Much better! The columns are all equal height and take up the full height of the page. In some sense this seems perfect, but there are a couple of minor downsides to this approach. One is browser support — currently every modern browser supports flexbox, but some older browsers never will. Fortunately browser vendors are making a bigger push to end support for these older browsers, making a more consistent development experience for web designers. Another downside is the fact that we needed to add the &lt;div class=&quot;container&quot;&gt; to the markup — it would be nice to avoid it. In an ideal world, any CSS layout wouldn’t require changing the HTML markup at all.</p><p>The biggest downside though is the code in the CSS itself — flexbox eliminates a lot of the float hacks, but the code isn’t as expressive as it could be for defining layout. It’s hard to read the flexbox CSS and get a visual understanding how all of the elements will be laid out on the page. This leads to a lot of guessing and checking when writing flexbox-based layouts.</p><p>It’s important to note again that flexbox was designed to space elements within a single column or row — it was not designed for an entire page layout! Even though it does a serviceable job (much better than float-based layouts), a different specification was specifically developed to handle layouts with multiple rows and columns. This specification is known as CSS grid.</p><h4>Grid-based layout</h4><p>CSS grid was first proposed in 2011 (not too long after the flexbox proposal), but took a long time to gain widespread adoption with browsers. As of early 2018, CSS grid is supported by most modern browsers (a huge improvement over even a year or two ago).</p><p>Below is a grid-based layout for our example based on the first method in this <a href="https://css-tricks.com/css-grid-one-layout-multiple-ways/">CSS tricks article</a>. Note that for this example, we can get rid of the &lt;div class=&quot;container&quot;&gt; that we had to add for the flexbox-based layout — we can simply use the original HTML without modification. Here’s what the CSS looks like:</p><pre>/* GRID-BASED LAYOUT */</pre><pre>body {<br>  display: grid;<br>  min-height: 100vh;<br>  grid-template-columns: 200px 1fr 150px;<br>  grid-template-rows: min-content 1fr min-content;<br>}</pre><pre>header {<br>  grid-row: 1;<br>  grid-column: 1 / 4;<br>}</pre><pre>nav {<br>  grid-row: 2;<br>  grid-column: 1 / 2;<br>  padding: 0 10px;<br>}</pre><pre>main {<br>  grid-row: 2;<br>  grid-column: 2 / 3;<br>  padding: 0 20px;<br>}</pre><pre>aside {<br>  grid-row: 2;<br>  grid-column: 3 / 4;<br>  padding: 0 10px;<br>}</pre><pre>footer {<br>  grid-row: 3;<br>  grid-column: 1 / 4;<br>}</pre><p>The result is visually identical to the flexbox based layout. However, the CSS here is much improved in the sense that it clearly expresses the desired layout. The size and shape of the columns and rows are defined in the body selector, and each item in the grid is defined directly by its position.</p><p>One thing that can be confusing is the grid-column property, which defines the start point / end point of the column. It can be confusing because in this example, there are 3 columns, but the numbers range from 1 to 4. It becomes more clear when you look at the picture below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GOJJrm9Mhp_Qr319Bc7V9Q.png" /><figcaption>Click <a href="https://codepen.io/peterxjang/pen/vdLROM?editors=1100">here</a> to see a live example</figcaption></figure><p>The first column starts at 1 and ends at 2, the second column starts at 2 and ends at 3, and the third column starts at 3 and ends at 4. The header has a grid-column of 1 / 4 to span the entire page, the nav has a grid-column of 1 / 2 to span the first column, etc.</p><p>Once you get used to the grid syntax, it clearly becomes the ideal way to express layout in CSS. The only real downside to a grid-based layout is browser support, which again has improved tremendously over the past year. It’s hard to overstate the importance of CSS grid as the first real tool in CSS that was actually designed for layout. In some sense, web designers have always had to be very conservative with making creative layouts, since the tools up until now have been fragile, using various hacks and workarounds. Now that CSS grid exists, there is the potential for a new wave of creative layout designs that never would have been possible before — exciting times!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JGkKSd3Hs7Pq17Qt5xTEzw.png" /></figure><h3>Using a CSS preprocessor for new syntax</h3><p>So far we’ve covered using CSS for basic styling as well as layout. Now we’ll get into tooling that was created to help improve the experience of working with CSS as a language itself, starting with CSS preprocessors.</p><p>A CSS preprocessor allows you to write styles using a different language which gets converted into CSS that the browser can understand. This was critical back in the day when browsers were very slow to implement new features. The first major CSS preprocessor was <a href="http://sass-lang.com/">Sass</a>, released in 2006. It featured a new concise syntax (indentation instead of brackets, no semicolons, etc.) and added advanced features missing from CSS, such as variables, helper functions, and calculations. Here’s what the color section of our earlier example would look like using Sass with variables:</p><pre>$dark-color: #4a4a4a<br>$light-color: #f9f9f9<br>$side-color: #eee</pre><pre>body<br>  color: $dark-color<br>  <br>header, footer<br>  background-color: $dark-color<br>  color: $light-color<br>  <br>main<br>  background: $light-color</pre><pre>nav, aside<br>  background: $side-color</pre><p>Note how reusable variables are defined with the $ symbol, and that brackets and semicolons are eliminated, making for a cleaner looking syntax. The cleaner syntax in Sass is nice, but features like variables were revolutionary at the time, as they opened up new possibilities for writing clean and maintainable CSS.</p><p>To use Sass, you need to <a href="https://www.ruby-lang.org/en/documentation/installation/">install Ruby</a>, the programming language used to compile Sass code to regular CSS. Then you would need to install the <a href="http://sass-lang.com/install">Sass gem</a>, then run a <a href="http://sass-lang.com/guide#topic-1">command in the command line</a> to convert your .sass files into .css files. Here’s an example of what a command would look like:</p><pre>sass --watch index.sass index.css</pre><p>This command will convert Sass code written in a file named index.sass to regular CSS in a file named index.css (the --watch argument tells it to run any time the input changes on save, which is convenient).</p><p>This process is known as a build step, and it was a pretty significant barrier to entry back in 2006. If you’re used to programming languages like Ruby, the process is pretty straightforward. But many frontend developers at the time only worked with HTML and CSS, which did not require any such tools. So it was a big ask to have someone learn an entire ecosystem to be able to get the features offered by a CSS preprocessor.</p><p>In 2009, the <a href="http://lesscss.org/">Less</a> CSS preprocessor was released. It was also written in Ruby, and offered similar features to Sass. The key difference was the syntax, which was designed to be as close to CSS as possible. This means that any CSS code is valid Less code. Here’s the same example written using Less syntax:</p><pre>@dark-color: #4a4a4a;<br>@light-color: #f9f9f9;<br>@side-color: #eee;</pre><pre>body {<br>  color: @dark-color;<br>}<br>  <br>header, footer {<br>  background-color: @dark-color;<br>  color: @light-color;<br>}<br>  <br>main {<br>  background: @light-color;<br>}</pre><pre>nav, aside {<br>  background: @side-color;<br>}</pre><p>It’s nearly the same (@ prefix instead of $ for variables), but not as pretty as the Sass example, with the same curly brackets and semi-colons as CSS. Yet the fact that it’s closer to CSS made it easier for developers to adopt it. In 2012, Less was rewritten to use JavaScript (specifically <a href="https://nodejs.org/en/">Node.js</a>) instead of Ruby for compiling. This made Less faster than its Ruby counterparts, and made it more appealing to developers who were already using Node.js in their workflows.</p><p>To convert this code to regular CSS, you would first need to <a href="https://nodejs.org/en/download/">install Node.js</a>, then <a href="http://lesscss.org/#using-less-installation">install Less</a>, then run a command like:</p><pre>lessc index.less index.css</pre><p>This command will convert Less code written in a file named index.less to regular CSS in a file named index.css. Note that the lessc command does not come with a way to watch files for changes (unlike the sass command), meaning you would need to install a different tool to automatically watch and compile .less files, adding a bit more complexity to the process. Again, this is not difficult for programmers who are used to using command line tools, but it is a significant barrier to entry for others who simply want to use a CSS preprocessor.</p><p>As Less gained mindshare, Sass developers adapted by adding a new syntax called <a href="http://sass-lang.com/documentation/file.SASS_CHANGELOG.html#SCSS__Sassy_CSS_">SCSS</a> in 2010 (which was a superset of CSS similar to Less). They also released <a href="http://sass-lang.com/libsass">LibSass</a>, a C/C++ port of the Ruby Sass engine, which made it faster and able to be used in various languages.</p><p>Another alternative CSS preprocessor is <a href="http://stylus-lang.com/">Stylus</a>, which came out in 2010, written in Node.js, and focuses on cleaner syntax compared to Sass or Less. Usually conversations about CSS preprocessors focus on those three as the most popular (Sass, Less, and Stylus). In the end, they are all pretty similar in terms of the features they offer, so you can’t really go wrong picking any of them.</p><p>However, some people make the argument that CSS preprocessors are becoming less necessary, as browsers are finally beginning to implement some of their features (such as variables and calculations). Furthermore, there’s a different approach known as CSS postprocessing that has the potential to make CSS preprocessors obsolete (obviously not without controversy), which we’ll get into next.</p><h3>Using a CSS postprocessor for transformative features</h3><p>A CSS postprocessor uses JavaScript to analyze and transform your CSS into valid CSS. In this sense it’s pretty similar to a CSS preprocessor — you can think of it as a different approach to solving the same problem. The key difference is that while a CSS preprocessor uses special syntax to identify what needs to be transformed, a CSS postprocessor can parse regular CSS and transform it without any special syntax required. This is best illustrated with an example. Let’s look at a part of the CSS we originally defined above to style the header tags:</p><pre>h1, h2, h3, h4, h5, h6 {<br><strong>  -ms-hyphens: auto;<br>  -moz-hyphens: auto;<br>  -webkit-hyphens: auto;</strong><br>  hyphens: auto;<br>}</pre><p>The items in bold are called vendor prefixes. Vendor prefixes are used by browsers when they are experimentally adding or testing new CSS features, giving a way for developers to use these new CSS properties while the implementation is being finalized. Here the -ms prefix is for Microsoft Internet Explorer, the -moz prefix is for Mozilla Firefox, and the -webkit prefix is for browsers using the webkit rendering engine (like Google Chrome, Safari, and newer versions of Opera).</p><p>It’s pretty annoying to remember to put in all these different vendor prefixes to use these CSS properties. It would be nice to have a tool that can automatically put in vendor prefixes as needed. We can sort of pull this off with CSS preprocessors. For example, you could do something like this with SCSS:</p><pre><strong>@mixin hyphens($value) {<br>  -ms-hyphens: </strong><strong>$value;<br>  -moz-hyphens: </strong><strong>$value;<br>  -webkit-hyphens: </strong><strong>$value;<br>  hyphens: </strong><strong>$value;</strong><strong><br>}</strong></pre><pre>h1, h2, h3, h4, h5, h6 {<strong><br>  @include hyphens(auto);</strong><br>}</pre><p>Here we’re using <a href="http://sass-lang.com/guide#topic-6">Sass’ mixin feature</a>, which allows you to define a chunk of CSS once and reuse it anywhere else. When this file is compiled into regular CSS, any @include statements will be replaced with the CSS from the matching @mixin. Overall this isn’t a bad solution, but you are responsible for defining each mixin the first time for any CSS property requiring vendor prefixes. These mixin definitions will require maintenance, as you may want to remove specific vendor prefixes that you no longer need as browsers update their CSS compatibility.</p><p>Instead of using mixins, it would be nice to simply write normal CSS and have a tool automatically identify properties that require prefixes and add them accordingly. A CSS postprocessor is capable of doing exactly that. For example, if you use <a href="http://postcss.org/">PostCSS</a> with the <a href="https://github.com/postcss/autoprefixer">autoprefixer plugin</a>, you can write completely normal CSS without any vendor prefixes and let the postprocessor do the rest of the work:</p><pre>h1, h2, h3, h4, h5, h6 {<strong><br>  hyphens: auto;</strong><br>}</pre><p>When you run the CSS postprocessor on this code, the result is the hyphens: auto; line gets replaced with all the appropriate vendor prefixes (as defined in the autoprefixer plugin, which you don’t need to directly manage). Meaning you can just write regular CSS without having to worry about any compatibility or special syntax, which is nice!</p><p>There are plugins other than autoprefixer for PostCSS that allow you to do really cool things. The <a href="http://cssnext.io/">cssnext</a> plugin allows you to use experimental CSS features. The <a href="https://github.com/css-modules/css-modules">CSS modules</a> plugin automatically changes classes to avoid name conflicts. The <a href="https://stylelint.io/">stylelint</a> plugin identifies errors and inconsistent conventions in your CSS. These tools have really started to take off in the last year or two, showcasing developer workflows that has never been possible before!</p><p>There is a price to pay for this progress, however. Installing and using a CSS postprocessor like PostCSS is more involved compared to using a CSS preprocessor. Not only do you have to install and run tools using the command line, but you need to install and configure individual plugins and define a more complex set of rules (like which browsers you are targeting, etc.) Instead of running PostCSS straight from the command line, many developers integrate it into configurable build systems like <a href="https://github.com/postcss/postcss#runners">Grunt</a>, <a href="https://github.com/postcss/postcss#gulp">Gulp</a>, or <a href="https://github.com/postcss/postcss#webpack">webpack</a>, which help manage all the different build tools you might use in your frontend workflow.</p><blockquote><strong>Note</strong>: It can be quite overwhelming to learn all the necessary parts to making a modern frontend build system work if you’ve never used one before. If you want to get started from scratch, check out my article <a href="https://medium.com/the-node-js-collection/modern-javascript-explained-for-dinosaurs-f695e9747b70">Modern JavaScript Explained For Dinosaurs</a>, which goes over all the JavaScript tooling necessary to take advantage of these modern features for a frontend developer.</blockquote><p>It’s worth noting that there is some debate around CSS postprocessors. Some argue that the terminology is confusing (<a href="https://css-tricks.com/the-trouble-with-preprocessing-based-on-future-specs/">one argument</a> is that they should all be called CSS preprocessors, <a href="https://medium.com/@ddprrt/deconfusing-pre-and-post-processing-d68e3bd078a3">another argument</a> is that they should just be simply called CSS processors, etc.). Some believe CSS postprocessors eliminate the need for CSS preprocessors altogether, some believe they should be used together. In any case, it’s clear that learning how to use a CSS postprocessor is worth it if you’re interested in pushing the edge of what’s possible with CSS.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*53X7_j093ePj9MOxrkA5sw.png" /></figure><h3>Using CSS methodologies for maintainability</h3><p>Tools like CSS preprocessors and CSS postprocessors go a long way towards improving the CSS development experience. But these tools alone aren’t enough to solve the problem of maintaining large CSS codebases. To address this, people began to document different guidelines on how to write CSS, generally referred to as CSS methodologies.</p><p>Before we dive into any particular CSS methodology, it’s important to understand what makes CSS hard to maintain over time. The key issue is the global nature of CSS — every style you define is globally applied to every part of the page. It becomes your job to either come up with a detailed naming convention to maintain unique class names or wrangle with <a href="https://www.smashingmagazine.com/2007/07/css-specificity-things-you-should-know/">specificity rules</a> to determine which style gets applied any given element. CSS methodologies provide an organized way to write CSS in order to avoid these pain points with large code bases. Let’s take a look at some of the popular methodologies in rough chronological order.</p><h4>OOCSS</h4><p><a href="https://github.com/stubbornella/oocss/wiki">OOCSS</a> (Object Oriented CSS) was first presented in 2009 as a methodology organized around two main principles. The first principle is <strong>separate structure and skin</strong>. This means the CSS to define the structure (like layout) shouldn’t be mixed together with the CSS to define the skin (like colors, fonts, etc.). This makes it easier to “re-skin” an application. The second principle is <strong>separate container and content</strong>. This means think of elements as re-usable objects, with the key idea being that an object should look the same regardless of where it is on the page.</p><p>OOCSS provides well thought out guidelines, but isn’t very prescriptive on the specifics of the approach. Later approaches like SMACSS took the core concepts and added more detail to make it easier to get started.</p><h4>SMACSS</h4><p><a href="https://smacss.com/">SMACSS</a> (Scalable and Modular Architecture for CSS) was introduced in 2011 as a methodology based around writing your CSS in 5 distinct categories — <strong>base rules</strong>, <strong>layout rules</strong>, <strong>modules</strong>, <strong>state rules</strong>, and <strong>theme rules</strong>. The SMACSS methodology also recommends some naming conventions. For layout rules, you would prefix class names with l- or layout-. For state rules, you would prefix class names that describe the state, like is-hidden or is-collapsed.</p><p>SMACSS has a lot more specifics in its approach compared to OOCSS, but it still requires some careful thought in deciding what CSS rules should go into which category. Later approaches like BEM took away some of this decision making to make it even easier to adopt.</p><h4>BEM</h4><p><a href="https://en.bem.info/">BEM</a> (Block, Element, Modifier) was introduced in 2010 as a methodology organized around the idea of dividing the user interface into independent blocks. A <strong>block</strong> is a re-usable component (an example would be a search form, defined as &lt;form class=&quot;search-form&quot;&gt;&lt;/form&gt;). An <strong>element</strong> is a smaller part of a block that can’t be re-used on its own (an example would be a button within the search form, defined as &lt;button class=&quot;search-form__button&quot;&gt;Search&lt;/button&gt;). A <strong>modifier</strong> is an entity that defines the appearance, state, or behavior of a block or element (an example would be a disabled search form button, defined as &lt;button class=&quot;search-form__button search-form__button--disabled&quot;&gt;Search&lt;/button&gt;).</p><p>The BEM methodology is simple to understand, with a specific naming convention that allows newcomers to apply it without having to make complex decisions. The downside for some is that the class names can be quite verbose, and don’t follow traditional rules for <a href="https://css-tricks.com/semantic-class-names/">writing semantic class names</a>. Later approaches like Atomic CSS would take this untraditional approach to a whole other level!</p><h4>Atomic CSS</h4><p><a href="https://www.smashingmagazine.com/2013/10/challenging-css-best-practices-atomic-approach/">Atomic CSS</a> (also known as Functional CSS) was introduced in 2014 as a methodology organized around the idea of creating small, single-purpose classes with names based on visual function. This approach is in complete opposition with OOCSS, SMACSS, and BEM — instead of treating elements on the page as re-usable objects, Atomic CSS ignores these objects altogether and uses re-usable single purpose utility classes to style each element. So instead of something like &lt;button class=&quot;search-form__button&quot;&gt;Search&lt;/button&gt;, you would have something like &lt;button class=&quot;f6 br3 ph3 pv2 white bg-purple hover-bg-light-purple&quot;&gt;Search&lt;/button&gt;.</p><p>If your first reaction to this example is to recoil in horror, you’re not alone — many people saw this methodology as a complete violation of established CSS best practices. However, there has been a lot of excellent discussion around the idea of questioning the effectiveness of those best practices in different scenarios. <a href="https://adamwathan.me/css-utility-classes-and-separation-of-concerns/">This article</a> does a great job highlighting how traditional separation of concerns ends up creating CSS that depends on the HTML (even when using methodologies like BEM), while an atomic or functional approach is about creating HTML that depends on the CSS. Neither is wrong, but upon close inspection you can see that a true separation of concerns between CSS and HTML is never fully achievable!</p><p>Other CSS methodologies like CSS in JS actually embrace the notion that CSS and HTML will always depend on each other, leading to one of the most controversial methodologies yet…</p><h4>CSS in JS</h4><p><a href="http://blog.vjeux.com/2014/javascript/react-css-in-js-nationjs.html">CSS in JS</a> was introduced in 2014 as a methodology organized around defining CSS styles not in a separate style sheet, but directly in each component itself. It was introduced as an approach for the <a href="https://reactjs.org/">React JavaScript framework</a> (which already took the controversial approach of defining the HTML for a component directly in JavaScript instead of a separate HTML file). Originally the methodology used inline styles, but later implementations used JavaScript to generate CSS (with unique class names based on the component) and insert it into the document with a style tag.</p><p>The CSS in JS methodology once again goes completely against established CSS best practices of separation of concerns. This is because the way we use the web has shifted dramatically over time. Originally the web largely consisted of static web sites — here the separation of HTML content from CSS presentation makes a lot of sense. Nowadays the web is used for creating dynamic web applications — here it makes sense to separate things out by re-usable components.</p><p>The goal of the CSS in JS methodology is to be able to define components with hard boundaries that consist of their own encapsulated HTML/CSS/JS, such that the CSS in one component has no chance of affecting any other components. React was one of the first widely adopted frameworks that pushed for these components with hard boundaries, influencing other major frameworks like Angular, Ember, and Vue.js to follow suit. It’s important to note that the CSS in JS methodology is relatively new, and there’s a lot of experimentation going on in this space as developers try to establish new best practices for CSS in the age of components for web applications.</p><p>It’s easy to get overwhelmed by the many different CSS methodologies that are out there, but it’s important to keep in mind that there is no one right approach — you should think of them as different possible tools you can use when you have a sufficiently complex CSS codebase. Having different well-thought-out options to choose from works in your favor, and all the recent experimentation happening in this space benefits every developer in the long run!</p><h3>Conclusion</h3><p>So this is modern CSS in a nutshell. We covered <strong>using CSS for basic styling </strong>with typographic properties, <strong>using CSS for layout</strong> using float, flexbox, and grid based approaches, <strong>using a CSS preprocessor for new syntax</strong> such as variables and mixins, <strong>using a CSS postprocessor for transformative features</strong> such as adding vendor prefixes, and <strong>using CSS methodologies for maintainability</strong> to overcome the global nature of CSS styles. We didn’t get a chance to dig into a lot of other features CSS has to offer, like advanced selectors, transitions, animations, shapes, dynamic variables — the list goes on and on. There’s a lot of ground to cover with CSS — anyone who says it’s easy probably doesn’t know the half of it!</p><p>Modern CSS can definitely be frustrating to work with as it continues to change and evolve at a rapid pace. But it’s important to remember the historical context of how the web has evolved over time, and it’s good to know that there are a lot of smart people out there willing to build concrete tools and methodologies to help CSS best practices evolve right along with the web. It’s an exciting time to be a developer, and I hope this information can serve as a roadmap to help you on your journey!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7Z_JtjrzLil6LFrM7lrNJQ.png" /></figure><p><em>Special thanks again to </em><a href="https://twitter.com/ryanqnorth"><em>@ryanqnorth</em></a><em>’s </em><a href="http://www.qwantz.com/"><em>Dinosaur Comics</em></a><em>, which has served up some of the finest absurdist humor since 2003 (when dinosaurs ruled the web).</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5226febe3525" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/modern-css-explained-for-dinosaurs-5226febe3525">Modern CSS Explained For Dinosaurs</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Comparing Frontend Approaches Part 7: Final Thoughts]]></title>
            <link>https://medium.com/actualize-network/comparing-frontend-approaches-part-7-final-thoughts-69cdba516f86?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/69cdba516f86</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[vuejs]]></category>
            <category><![CDATA[jquery]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[elm]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Sat, 13 Jan 2018 23:14:46 GMT</pubDate>
            <atom:updated>2018-08-29T12:51:26.208Z</atom:updated>
            <content:encoded><![CDATA[<h4>A look at jQuery, Vue.js, React, and Elm</h4><ul><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">Part 1: Introduction</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">Part 2: jQuery</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">Part 3: Vue.js</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Part 4: Vue.js with components</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-5-react-b51fd7d075fe">Part 5: React</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-6-elm-578714526164">Part 6: Elm</a></li><li><strong>Part 7: Final thoughts</strong></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*i1kwW5Jx_F3nYv0AsPlsXg.jpeg" /></figure><p>If you’ve made it this far, congratulations! Here’s a brief summary of the pros and cons of each approach for building this particular notes app:</p><h4>jQuery</h4><p>A direct DOM manipulation based approach. <br><strong>Pros</strong>: Easy to install, easy to learn. Smaller in size than most frameworks.<br><strong>Cons</strong>: Requires diligence with organizing code. Making all appropriate DOM updates when something changes can get complex (“jQuery spaghetti”).</p><h4>Vue.js</h4><p>A template based approach (with a virtual DOM under the hood)<br><strong>Pros</strong>: Easy to install, easy to learn. Smallest size in terms lines of code, one of the smallest in terms of payload.<br><strong>Cons</strong>: Non-trivial amount of Vue specific syntax to learn. Can get harder to organize code in the single file and Vue instance as the app increases in complexity.</p><h4>Vue.js with components</h4><p>The same Vue.js approach, this time with components<br><strong>Pros</strong>: Single file components reduces cognitive load. Increased reusability and simplicity with components.<br><strong>Cons</strong>: Requires complex build step. Passing data down and bubbling events up can be tedious with multiple parent-child components.</p><h4>React</h4><p>A virtual DOM based component approach<strong><br>Pros</strong>: Small API surface. Immutable state improves simplicity. Increased reusability and simplicity with components.<br><strong>Cons</strong>: Requires complex build step. Harder to learn with heavy use of advanced and experimental JS features. Functional approach in JS requires particular care.</p><h4>Elm</h4><p>A statically typed, purely functional approach with virtual DOM<br><strong>Pros</strong>: Static types and immutable data prevent runtime errors. Clear code structure with The Elm Architecture. Smallest payload (as of version 0.19).<br><strong>Cons</strong>: Harder to learn (a totally separate language from JS). Side effects require extra work to wire up.</p><p>Note that the purpose here has never been to help you choose which frontend approach is “best”. The answer is always it depends on the context. For an app this small in scale, Vue.js (without components) seems like a good fit, especially since it has the smallest payload and the code is well structured. <em>(Edit: Elm version 0.19 has made a lot of </em><a href="http://elm-lang.org/blog/small-assets-without-the-headache"><em>optimizations to reduce payload size</em></a><em> and currently is significantly smaller than all other approaches here!)</em> But most apps tend to increase in scope, and even your most fundamental assumptions can change as you add, remove, or modify features.</p><p>Context also includes a person’s current level of knowledge. What’s easy for you might be hard for someone else, and vice versa. What’s hard for you today may be easy for you a year from now. This means Elm might be a terrible choice for someone with a tight deadline and no experience with functional programming, and yet would be a perfect choice for someone already experienced with Elm. All frontend approaches covered here have great merit, and the honest best choice has more to do with your team and company culture than it does with some definitive “winner”.</p><p>Going back to the original idea of learning a new programming language a year, my hope is that you gained a better understanding of how each frontend approach works. This will help you write better code, regardless of your choice of library or framework. I also hope this inspired you to dig deeper into a frontend approach, whether you’re new to functional programming or a React developer who never learned jQuery. Thanks for reading!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=69cdba516f86" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/comparing-frontend-approaches-part-7-final-thoughts-69cdba516f86">Comparing Frontend Approaches Part 7: Final Thoughts</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Comparing Frontend Approaches Part 6: Elm]]></title>
            <link>https://medium.com/actualize-network/comparing-frontend-frameworks-part-6-elm-578714526164?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/578714526164</guid>
            <category><![CDATA[elm]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Sat, 13 Jan 2018 23:14:29 GMT</pubDate>
            <atom:updated>2018-08-29T17:09:48.863Z</atom:updated>
            <content:encoded><![CDATA[<h4>A look at jQuery, Vue.js, React, and Elm</h4><ul><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">Part 1: Introduction</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">Part 2: jQuery</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">Part 3: Vue.js</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Part 4: Vue.js with components</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-5-react-b51fd7d075fe">Part 5: React</a></li><li><strong>Part 6: Elm</strong></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-approaches-part-7-final-thoughts-69cdba516f86">Part 7: Final thoughts</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*NQppPMuv7xYB1IB4hF4AnQ.png" /></figure><p>In this part we will be implementing the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">web based clone</a> of the <a href="https://support.apple.com/kb/PH22608?locale=en_US">Mac Notes app</a> using <a href="http://elm-lang.org/">Elm</a>. Elm (which is not a JavaScript framework but a completely different language that compiles down to JavaScript) came out in 2012 and is currently gaining a lot of traction, with more and more companies <a href="https://github.com/lpil/elm-companies">using it in production</a>.</p><p>Elm is a statically typed purely functional language inspired by Haskell, but with a design that makes it easier for beginners to functional programming to get started. It uses a virtual DOM approach similar to React (even though the two were created independently). Elm doesn’t use a framework per se, but instead uses a pattern called <a href="https://guide.elm-lang.org/architecture/">The Elm Architecture</a>. Let’s dive in and check it out!</p><blockquote>Note — this article is not meant to teach Elm from scratch, but rather give an overview of what building an Elm app looks like in comparison to other JavaScript frontend approaches. You don’t need experience with the language to get something out of the article, but if you want a sense of the language before continuing, I would recommend reading this <a href="http://elm-lang.org/docs/from-javascript">Elm vs. JavaScript syntax comparison</a> and looking over the <a href="https://guide.elm-lang.org/">official guide</a> (which is an excellent resource for learning the language).</blockquote><h3>Installation</h3><p>Like <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Vue.js with components</a> and <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-5-react-b51fd7d075fe">React</a>, we’re going to need a build process to use Elm. First we need to install Elm itself if you don’t already have it on your computer:</p><pre>$ npm install -g elm</pre><p>Now we’re going to create a new directory and copy the HTML and CSS files from the original <a href="https://jsfiddle.net/peterxjang/vtsjc5w9/?utm_source=website&amp;utm_medium=embed&amp;utm_campaign=vtsjc5w9">notes app template</a>. The only thing left is the JavaScript, which we will generate from Elm code. First run the command:</p><pre>$ elm init</pre><p>This will prompt you to create an elm.json file (which is similar to JavaScript’s package.json file). By default it included the necessary dependencies for Elm to work with a web browser.</p><p>Next we’re going to create a new file called Notes.elm, which will contain our Elm code. Right now we aren’t going to add any new functionality, we just want to convert the HTML template into Elm code, like we did with <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">React</a> and JSX. However, Elm doesn’t use JSX — it uses plain old functions instead. Here’s what the <strong>Notes.elm</strong> file looks like:</p><pre>module Notes exposing (main)<br><br>import Html exposing (..)<br>import Html.Attributes exposing (..)<br><br><br>main =<br>    div [ id &quot;app&quot; ]<br>        [ div [ class &quot;toolbar&quot; ]<br>            [ button [ class &quot;toolbar-button&quot; ] [ text &quot;New&quot; ]<br>            , button [ class &quot;toolbar-button&quot; ] [ text &quot;Delete&quot; ]<br>            , input [ class &quot;toolbar-search&quot;, type_ &quot;text&quot;, placeholder &quot;Search...&quot; ] []<br>            ]<br>        , div [ class &quot;note-container&quot; ]<br>            [ div [ class &quot;note-selectors&quot; ]<br>                [ div [ class &quot;note-selector active&quot; ]<br>                    [ p [ class &quot;note-selector-title&quot; ] [ text &quot;First note...&quot; ]<br>                    , p [ class &quot;note-selector-timestamp&quot; ] [ text &quot;Timestamp here...&quot; ]<br>                    ]<br>                , div [ class &quot;note-selector&quot; ]<br>                    [ p [ class &quot;note-selector-title&quot; ] [ text &quot;Second note...&quot; ]<br>                    , p [ class &quot;note-selector-timestamp&quot; ] [ text &quot;Timestamp here...&quot; ]<br>                    ]<br>                , div [ class &quot;note-selector&quot; ]<br>                    [ p [ class &quot;note-selector-title&quot; ] [ text &quot;Third note...&quot; ]<br>                    , p [ class &quot;note-selector-timestamp&quot; ] [ text &quot;Timestamp here...&quot; ]<br>                    ]<br>                ]<br>            , div [ class &quot;note-editor&quot; ]<br>                [ p [ class &quot;note-editor-info&quot; ] [ text &quot;Timestamp here...&quot; ]<br>                , textarea [ class &quot;note-editor-input&quot; ] [ text &quot;First note...&quot; ]<br>                ]<br>            ]<br>        ]</pre><p>This is our first look at Elm code! Some notes:</p><ul><li>The first line is defining the code in this file as a module named Notes, which we will refer to later in the HTML.</li><li>The next two lines are importing functions from the Html library.</li><li>The next line main = is defining the main function. Functions in Elm do not have parentheses or keyword declarations, so they’re a bit hard to notice when coming from JavaScript.</li><li>The rest of the code is defining the HTML using functions like div, button, p, etc. Each of these functions take two arguments — a list of attributes and a list of child nodes. So for example, a simple &lt;p&gt; tag would look like p [class &quot;info&quot;] [text &quot;Sample text&quot;]. This app starts with the parent div [ id &quot;app&quot; ] [ … ], and every other HTML element is a child node of this parent and are inside the second argument list (which is a bit hard to see here).</li><li>The <a href="http://elm-lang.org/docs/style-guide">convention in Elm</a> is to write multi-line lists with commas at the start of each line instead of at the end, which can be pretty jarring coming from languages like JavaScript. The good news is that you can install a tool called <a href="https://github.com/avh4/elm-format">elm-format</a>, which can automatically format your Elm code (similar to <a href="https://golang.org/cmd/gofmt/">gofmt</a> for Go and <a href="https://github.com/prettier/prettier">prettier</a> for JavaScript). You can configure it to format your code every time you hit save in your editor, so you don’t need to worry about comma placement, indentation, or other style concerns!</li></ul><p>To compile the code, we need to run this command in the terminal:</p><pre>$ elm make Notes.elm --output js/notes.js</pre><p>This will use the elm make command line tool to compile the Elm code in Notes.elm into a JavaScript file named notes.js in a folder named js. Note that it will also download the appropriate packages to a new folder called elm-stuff (similar to Node’s node_modules folder).</p><p>The last step is to edit the index.html file by removing all the HTML elements that now live in the Elm code and adding code to load up the Elm library:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>&lt;head&gt;<br>  &lt;meta charset=&quot;UTF-8&quot;&gt;<br>  &lt;title&gt;Notes&lt;/title&gt;<br>  &lt;link rel=&quot;stylesheet&quot; href=&quot;css/notes.css&quot;&gt;&lt;/link&gt;<br>  &lt;script src=&quot;js/notes.js&quot;&gt;&lt;/script&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br><strong>  &lt;div id=&quot;app&quot;&gt;&lt;/div&gt;<br>  &lt;script&gt;<br>    var app = Elm.Notes.init({<br>      node: document.querySelector(&#39;#app&#39;)<br>    });<br>  &lt;/script&gt;</strong><br>&lt;/body&gt;<br>&lt;/html&gt;</pre><p>Open the index.html file in your browser and you should see the familiar notes app starting point! You can see the code so far <a href="https://github.com/peterxjang/notes-app-elm/tree/7913b8708a09c4482b307cd9bb47ae8f3f6ed8c3">here</a> for reference.</p><h3>Display note titles from an array of notes</h3><p>Now we can start to make the HTML more dynamic by storing notes as a list (think JS array) of records (think JS objects) and looping through them to generate note selectors. We will do this by structuring the code using <a href="https://guide.elm-lang.org/architecture">The Elm Architecture</a>, which essentially boils down to defining 4 functions that Elm wires together to create the app. Here’s what the code in <strong>Notes.elm</strong> looks like, which we will break down in more detail:</p><pre>module Notes exposing (main)</pre><pre>import Browser<br>import Html exposing (..)<br>import Html.Attributes exposing (..)</pre><pre>main =<br>    Browser.element<br>        { init = init<br>        , view = view<br>        , update = update<br>        , subscriptions = subscriptions<br>        }</pre><pre>-- MODEL</pre><pre>type alias Note =<br>    { id : Int, body : String, timestamp : Int }</pre><pre>type alias Model =<br>    { notes : List Note }</pre><pre>init : () -&gt; ( Model, Cmd Msg )<br>init _ =<br>    ( { notes =<br>            [ { id = 1, body = &quot;First note...&quot;, timestamp = 0 }<br>            , { id = 2, body = &quot;Second note...&quot;, timestamp = 0 }<br>            , { id = 3, body = &quot;Third note...&quot;, timestamp = 0 }<br>            ]<br>      }<br>    , Cmd.none<br>    )</pre><pre>-- UPDATE</pre><pre>type Msg<br>    = NoOp</pre><pre>update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )<br>update msg model =<br>    ( model, Cmd.none )</pre><pre>-- SUBSCRIPTIONS</pre><pre>subscriptions : Model -&gt; Sub Msg<br>subscriptions model =<br>    Sub.none</pre><pre>-- VIEW</pre><pre>view : Model -&gt; Html Msg<br>view model =<br>    div [ id &quot;app&quot; ]<br>        [ div [ class &quot;toolbar&quot; ]<br>            [ button [ class &quot;toolbar-button&quot; ] [ text &quot;New&quot; ]<br>            , button [ class &quot;toolbar-button&quot; ] [ text &quot;Delete&quot; ]<br>            , input [ class &quot;toolbar-search&quot;, type_ &quot;text&quot;, placeholder &quot;Search...&quot; ] []<br>            ]<br>        , div [ class &quot;note-container&quot; ]<br>            [ viewNoteSelectors model<br>            , div [ class &quot;note-editor&quot; ]<br>                [ p [ class &quot;note-editor-info&quot; ] [ text &quot;Timestamp here...&quot; ]<br>                , textarea [ class &quot;note-editor-input&quot; ] [ text &quot;First note...&quot; ]<br>                ]<br>            ]<br>        ]</pre><pre>viewNoteSelectors : Model -&gt; Html Msg<br>viewNoteSelectors model =<br>    div [ class &quot;note-selectors&quot; ]<br>        (List.map (\note -&gt; viewNoteSelector note) model.notes)</pre><pre>viewNoteSelector : Note -&gt; Html Msg<br>viewNoteSelector note =<br>    div [ class &quot;note-selector&quot; ]<br>        [ p [ class &quot;note-selector-title&quot; ] [ text note.body ]<br>        , p [ class &quot;note-selector-timestamp&quot; ] [ text (String.fromInt note.timestamp) ]<br>        ]</pre><p>This definitely can be confusing the first time looking at it — let’s break it down in sections. First let’s look at the main function itself:</p><pre>main =<br>    Browser.element<br>        { init = init<br>        , view = view<br>        , update = update<br>        , subscriptions = subscriptions<br>        }</pre><p>Here we’re using the Browser.element function that does all the work of wiring up our app. All we need to do is define each of the 4 functions — init, view, update, and subscriptions.</p><p>Before we look at the 4 functions, let’s first look at how we’re defining the model, which is the data we’re using in our app (think state in React or data in Vue.js):</p><pre>-- MODEL<br><br><br>type alias Note =<br>    { id : Int, body : String, timestamp : Int }<br><br><br>type alias Model =<br>    { notes : List Note }</pre><p>Elm is a statically typed language, so we are defining the types on all the data up front. An individual Note type is defined with the line type alias Note = { id : Int, body : String, timestamp : Int }, which means a note must be a record with an integer id, string body, and integer timestamp. Now we define the Model itself with the line type alias Model = { notes : List Note }. This is saying that the Model must be a record with a key of notes and a value of a list of Note records. Defining these static types up front can seem like extra work, but they help prevent runtime errors later (if you forget to add a timestamp to a new note, etc.).</p><p>Now let’s take a look at the init function, which initializes the model:</p><pre>init : () -&gt; ( Model, Cmd Msg )<br>init _ =<br>    ( { notes =<br>            [ { id = 1, body = &quot;First note...&quot;, timestamp = 0 }<br>            , { id = 2, body = &quot;Second note...&quot;, timestamp = 0 }<br>            , { id = 3, body = &quot;Third note...&quot;, timestamp = 0 }<br>            ]<br>      }<br>    , Cmd.none<br>    )</pre><p>The init function itself has a type annotation of init : () -&gt; ( Model, Cmd Msg ), which means it must return a Model and a Cmd (command). Commands are how Elm handles side effects, which is a significant departure from the world of JavaScript. Elm is a pure functional language, which means functions aren’t allowed to have side effects, so you need different mechanisms to handle them. As of this moment, the init function doesn’t have any need for side effects so we’re outputting no command with Cmd.none. Also note that I’m leaving the timestamps as 0 for now — we need some sort of Elm equivalent to JavaScript’s Date.now(). But if you consider it closely, getting the current time is an impure function with side effects! Which means we actually will need a command in the init function, which we’ll wire up in a bit…</p><p>The next function is the update function, which defines how the model gets updated:</p><pre>-- UPDATE<br><br><br>type Msg<br>    = NoOp<br><br><br>update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )<br>update msg model =<br>    ( model, Cmd.none )</pre><p>Right now the update function isn’t doing anything, so this is mostly placeholder code for now. The type annotation for the update function is update : Msg -&gt; Model -&gt; ( Model, Cmd Msg ), which says the function takes in two arguments (a Msg and a Model) and returns a tuple of type ( Model, Cmd Msg ), which contains the updated model and a command for side effects. Right now our app doesn’t have any messages, so we have the line type Msg = NoOp as a placeholder for now — eventually we’ll have messages for selecting notes, clicking on the new button, etc.</p><p>The next function is the subscriptions function, which are used to listen to events:</p><pre>-- SUBSCRIPTIONS<br><br><br>subscriptions : Model -&gt; Sub Msg<br>subscriptions model =<br>    Sub.none</pre><p>Again, we’re not using subscriptions right now, so this code is essentially doing nothing. Subscriptions are for listening to events like mouse movements, web sockets, etc., which we won’t need for this app.</p><p>The final function is the view function, which defines the HTML output:</p><pre>-- VIEW</pre><pre>view : Model -&gt; Html Msg<br>view model =<br>    div [ id &quot;app&quot; ]<br>        [ div [ class &quot;toolbar&quot; ]<br>            [ button [ class &quot;toolbar-button&quot; ] [ text &quot;New&quot; ]<br>            , button [ class &quot;toolbar-button&quot; ] [ text &quot;Delete&quot; ]<br>            , input [ class &quot;toolbar-search&quot;, type_ &quot;text&quot;, placeholder &quot;Search...&quot; ] []<br>            ]<br>        , div [ class &quot;note-container&quot; ]<br>            [ viewNoteSelectors model<br>            , div [ class &quot;note-editor&quot; ]<br>                [ p [ class &quot;note-editor-info&quot; ] [ text &quot;Timestamp here...&quot; ]<br>                , textarea [ class &quot;note-editor-input&quot; ] [ text &quot;First note...&quot; ]<br>                ]<br>            ]<br>        ]</pre><pre>viewNoteSelectors : Model -&gt; Html Msg<br>viewNoteSelectors model =<br>    div [ class &quot;note-selectors&quot; ]<br>        (List.map (\note -&gt; viewNoteSelector note) model.notes)</pre><pre>viewNoteSelector : Note -&gt; Html Msg<br>viewNoteSelector note =<br>    div [ class &quot;note-selector&quot; ]<br>        [ p [ class &quot;note-selector-title&quot; ] [ text note.body ]<br>        , p [ class &quot;note-selector-timestamp&quot; ] [ text (String.fromInt note.timestamp) ]<br>        ]</pre><p>This is the same code we started with to generate the HTML, broken down into sub-functions. The view function type annotation is view : Model -&gt; Html Msg, which means it takes in the model (our list of notes) as the argument and outputs HTML.</p><p>Unlike our <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-5-react-b51fd7d075fe">React approach</a>, we aren’t going to break this app into components up front, or ever for that matter. We will be breaking things up into functions, but only when there’s a need to do so. In this case I made a sub-function viewNoteSelectors which is responsible for generating the &lt;div class=&quot;note-selectors&quot;&gt; HTML. It does this by mapping over the list of notes and calling up the viewNoteSelector function, which takes in a note and outputs the &lt;div class=&quot;note-selector&quot;&gt; HTML.</p><p>Although this seems like a lot of code to get started, keep in mind that every Elm app will consist of the same 4 functions (init, view, update, and subscriptions). This takes away a lot of the decision fatigue that comes with building frontend apps.</p><h3>Compute current timestamps</h3><p>Now let’s circle back and properly compute the current timestamps. This was trivial in JavaScript, but since it involves side effects it takes more effort to wire up in Elm. First we need to install a new package to handle time:</p><pre>$ elm install elm/time</pre><p>Once it’s installed, you would change the code in <strong>Notes.elm</strong> as follows (I’m not showing the subscriptions and view functions since they didn’t change):</p><pre>module Notes exposing (main)</pre><pre>import Browser<br>import Html exposing (..)<br>import Html.Attributes exposing (..)<br><strong>import Task<br>import Time</strong></pre><pre>main =<br>    Browser.element<br>        { init = init<br>        , view = view<br>        , update = update<br>        , subscriptions = subscriptions<br>        }</pre><pre>-- MODEL</pre><pre>type alias Note =<br>    { id : Int, body : String, timestamp : Int }</pre><pre>type alias Model =<br>    { notes : List Note }</pre><pre>init : () -&gt; ( Model, Cmd Msg )<br>init _ =<br>    ( { notes =<br>            [ { id = 1, body = &quot;First note...&quot;, timestamp = 0 }<br>            , { id = 2, body = &quot;Second note...&quot;, timestamp = 0 }<br>            , { id = 3, body = &quot;Third note...&quot;, timestamp = 0 }<br>            ]<br>      }<br>    , <strong>Task.perform InitializeNotesTimestamps Time.now</strong><br>    )</pre><pre>-- UPDATE</pre><pre>type Msg<br>    = <strong>InitializeNotesTimestamps Time.Posix</strong></pre><pre>update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )<br>update msg model =<br><strong>    case msg of<br>        InitializeNotesTimestamps time -&gt;<br>            ( { model<br>                | notes = List.map (\note -&gt; { note | timestamp = Time.posixToMillis time }) model.notes<br>              }<br>            , Cmd.none<br>            )</strong></pre><pre>-- SUBSCRIPTIONS<br>-- ...</pre><pre>-- VIEW<br>-- ...</pre><p>Let’s break this down piece by piece:</p><ul><li>First we import the Task and Time libraries, which are needed to create timestamps.</li><li>The init function now runs a command with the <a href="http://package.elm-lang.org/packages/elm-lang/core/latest/Task#perform">Task.perform</a> function, which takes in two arguments — in this case the InitializeTimestamps message and <a href="http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Time#now">Time.now</a>, which gets the POSIX representation of time when the task is run (similar to JavaScript’s Date.now(), although it isn’t stored in Elm as an integer).</li><li>The update section now has something to do! First we change the Msg definition to be a message called InitializeNotesTimestamps (which our init function will send out), which comes with the time result from Time.now.</li><li>Now we can change the update function itself to respond to the case where the message is InitializeNotesTimestamps (right now this is the only possibility, but we will be adding more cases soon). In that case the function will update every note with the given time (which in this case must first be converted into milliseconds using Time.posixToMillis).</li></ul><p>There’s no doubt that this is quite a bit of work in Elm for something that was trivial in JavaScript. Consider this an up front cost — this architecture provides some absolute guarantees about the safety and structure of the code. The purity in a functional language like Elm makes certain features with side effects harder to implement, but makes it literally impossible to be lazy and write something like jQuery spaghetti.</p><h3>Use functions to sort and format notes</h3><p>Now let’s write more functions, this time to sort and format the notes. Here’s the code in <strong>Notes.elm</strong> (focusing on the view function):</p><pre>module Notes exposing (main)</pre><pre>import Browser<br>import Html exposing (..)<br>import Html.Attributes exposing (..)<br>import Task<br>import Time</pre><pre>main =<br>    Browser.element<br>        { init = init<br>        , view = view<br>        , update = update<br>        , subscriptions = subscriptions<br>        }</pre><pre>-- MODEL<br>-- ...</pre><pre><br>-- UPDATE<br>-- ...</pre><pre><br>-- SUBSCRIPTIONS<br>-- ...</pre><pre><br>-- VIEW</pre><pre>view : Model -&gt; Html Msg<br>view model =<br>    div [ id &quot;app&quot; ]<br>        [ div [ class &quot;toolbar&quot; ]<br>            [ button [ class &quot;toolbar-button&quot; ] [ text &quot;New&quot; ]<br>            , button [ class &quot;toolbar-button&quot; ] [ text &quot;Delete&quot; ]<br>            , input [ class &quot;toolbar-search&quot;, type_ &quot;text&quot;, placeholder &quot;Search...&quot; ] []<br>            ]<br>        , div [ class &quot;note-container&quot; ]<br>            [ viewNoteSelectors model<br>            , div [ class &quot;note-editor&quot; ]<br>                [ p [ class &quot;note-editor-info&quot; ] [ text &quot;Timestamp here...&quot; ]<br>                , textarea [ class &quot;note-editor-input&quot; ] [ text &quot;First note...&quot; ]<br>                ]<br>            ]<br>        ]</pre><pre>viewNoteSelectors : Model -&gt; Html Msg<br>viewNoteSelectors model =<br>    div [ class &quot;note-selectors&quot; ]<br><strong>        (model.notes<br>            |&gt; List.sortBy .timestamp<br>            |&gt; List.reverse<br>            |&gt; List.map (\note -&gt; viewNoteSelector note)<br>        )</strong></pre><pre>viewNoteSelector : Note -&gt; Html Msg<br>viewNoteSelector note =<br>    div [ class &quot;note-selector&quot; ]<br>        [ p [ class &quot;note-selector-title&quot; ] [ text <strong>(formatTitle note.body)</strong> ]<br>        , p [ class &quot;note-selector-timestamp&quot; ] [ text <strong>(formatTimestamp note.timestamp)</strong> ]<br>        ]</pre><pre><strong>-- HELPERS</strong></pre><pre><strong>formatTitle : String -&gt; String<br>formatTitle body =<br>    let<br>        maxLength =<br>            20<br>        length =<br>            String.length body<br>    in<br>    if length &gt; maxLength then<br>        String.left (maxLength - 3) body ++ &quot;...&quot;<br>    else if length == 0 then<br>        &quot;New note&quot;<br>    else<br>        body</strong></pre><pre><strong>formatTimestamp : Int -&gt; String<br>formatTimestamp timestamp =<br>    let<br>        time =<br>            Time.millisToPosix timestamp<br>        hour =<br>            String.fromInt (Time.toHour Time.utc time)<br>        minute =<br>            String.fromInt (Time.toMinute Time.utc time)<br>        second =<br>            String.fromInt (Time.toSecond Time.utc time)<br>    in<br>    hour ++ &quot;:&quot; ++ minute ++ &quot;:&quot; ++ second ++ &quot; UTC&quot;</strong></pre><p>Let’s go over the changes one at a time:</p><ul><li>In the viewNoteSelectors function, we are adding extra function calls to sort the notes by timestamp in reverse order. Note the use of the |&gt; operator, which is an alternative syntax for passing arguments to functions ((arg |&gt; func) is the same as (func arg)), which helps reduce parentheses.</li><li>In the viewNoteSelector function, we are using the formatTitle and formatTimestamp functions, defined below.</li><li>The formatTitle function is using the same logic we’ve used in the past. Note the use of the let...in... syntax, which is required when creating temporary variables in a function.</li><li>The formatTimestamp function uses the Date library to format the given timestamp (like JavaScript’s .toUTCString()). I’m also using String.slice to remove parts of the formatted string that aren’t needed.</li></ul><p>Note that the formatted timestamp isn’t as detailed as JavaScript’s .toUTCString(). Elm doesn’t come with an equivalent function, so I’m using a simplified formatting helper function that only returns the hours/minutes/seconds. To make it more detailed can get quite involved (getting the <a href="https://package.elm-lang.org/packages/elm/time/latest/Time#here">user’s time zone</a>, formatting <a href="https://package.elm-lang.org/packages/elm/time/latest/Time#Weekday">weekday</a>, <a href="https://package.elm-lang.org/packages/elm/time/latest/Time#Month">month names</a>, etc.), which is outside the scope of this article, so the simplified function will do for now.</p><h3>Select a note on title click</h3><p>Now let’s implement the ability to actually select notes. Clicking on a note title should both highlight the selected note on the left as well as display the contents in the editor on the right. The code in <strong>Notes.elm</strong> will change accordingly:</p><pre>module Notes exposing (main)<br><br>import Browser<br>import Html exposing (..)<br>import Html.Attributes exposing (..)<br><strong>import Html.Events exposing (..)</strong><br>import Task<br>import Time<br><br><br>main =<br>    Browser.element<br>        { init = init<br>        , view = view<br>        , update = update<br>        , subscriptions = subscriptions<br>        }<br><br><br><br>-- MODEL<br><br><br>type alias Note =<br>    { id : Int, body : String, timestamp : Int }<br><br><br>type alias Model =<br>    <strong>{ notes : List Note, selectedNoteId : Int }</strong><br><br><br>init : () -&gt; ( Model, Cmd Msg )<br>init _ =<br>    ( { notes =<br>            [ { id = 1, body = &quot;First note...&quot;, timestamp = 0 }<br>            , { id = 2, body = &quot;Second note...&quot;, timestamp = 0 }<br>            , { id = 3, body = &quot;Third note...&quot;, timestamp = 0 }<br>            ]<br>      <strong>, selectedNoteId = 1</strong><br>      }<br>    , Task.perform InitializeNotesTimestamps Time.now<br>    )<br><br><br><br>-- UPDATE<br><br><br>type Msg<br>    = InitializeNotesTimestamps Time.Posix<br>    <strong>| SelectNote Int</strong><br><br><br>update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )<br>update msg model =<br>    case msg of<br>        InitializeNotesTimestamps time -&gt;<br>            ( { model<br>                | notes = List.map (\note -&gt; { note | timestamp = Time.posixToMillis time }) model.notes<br>              }<br>            , Cmd.none<br>            )</pre><pre><strong>        SelectNote id -&gt;<br>            ( { model | selectedNoteId = id }, Cmd.none )</strong><br><br><br><br>-- SUBSCRIPTIONS<br><br><br>subscriptions : Model -&gt; Sub Msg<br>subscriptions model =<br>    Sub.none<br><br><br><br>-- VIEW<br><br><br>view : Model -&gt; Html Msg<br>view model =<br>    div [ id &quot;app&quot; ]<br>        [ div [ class &quot;toolbar&quot; ]<br>            [ button [ class &quot;toolbar-button&quot; ] [ text &quot;New&quot; ]<br>            , button [ class &quot;toolbar-button&quot; ] [ text &quot;Delete&quot; ]<br>            , input [ class &quot;toolbar-search&quot;, type_ &quot;text&quot;, placeholder &quot;Search...&quot; ] []<br>            ]<br>        , div [ class &quot;note-container&quot; ]<br>            [ viewNoteSelectors model<br>            <strong>, viewNoteEditor model</strong><br>            ]<br>        ]<br><br><br>viewNoteSelectors : Model -&gt; Html Msg<br>viewNoteSelectors model =<br>    div [ class &quot;note-selectors&quot; ]<br>        (model.notes<br>            |&gt; List.sortBy .timestamp<br>            |&gt; List.reverse<br>            <strong>|&gt; List.map (\note -&gt; viewNoteSelector note model.selectedNoteId)</strong><br>        )<br><br><br><strong>viewNoteSelector : Note -&gt; Int -&gt; Html Msg<br>viewNoteSelector note selectedNoteId =</strong><br><strong>    div [ classList [ ( &quot;note-selector&quot;, True ), ( &quot;active&quot;, note.id == selectedNoteId ) ], onClick (SelectNote note.id) ]</strong><br>        [ p [ class &quot;note-selector-title&quot; ] [ text (formatTitle note.body) ]<br>        , p [ class &quot;note-selector-timestamp&quot; ] [ text (formatTimestamp note.timestamp) ]<br>        ]<br><br><br><strong>viewNoteEditor : Model -&gt; Html Msg<br>viewNoteEditor model =<br>    case model.notes |&gt; List.filter (\note -&gt; note.id == model.selectedNoteId) |&gt; List.head of<br>        Nothing -&gt;<br>            div [ class &quot;note-editor&quot; ] []<br><br>        Just selectedNote -&gt;<br>            div [ class &quot;note-editor&quot; ]<br>                [ p [ class &quot;note-editor-info&quot; ] [ text (formatTimestamp selectedNote.timestamp) ]<br>                , textarea [ class &quot;note-editor-input&quot; ] [ text selectedNote.body ]<br>                ]</strong></pre><pre>-- HELPERS</pre><pre>formatTitle : String -&gt; String<br>formatTitle body =<br>    let<br>        maxLength =<br>            20</pre><pre>    length =<br>            String.length body<br>    in<br>    if length &gt; maxLength then<br>        String.left (maxLength - 3) body ++ &quot;...&quot;</pre><pre>    else if length == 0 then<br>        &quot;New note&quot;</pre><pre>    else<br>        body</pre><pre>formatTimestamp : Int -&gt; String<br>formatTimestamp timestamp =<br>    let<br>        time =<br>            Time.millisToPosix timestamp</pre><pre>        hour =<br>            String.fromInt (Time.toHour Time.utc time)</pre><pre>        minute =<br>            String.fromInt (Time.toMinute Time.utc time)</pre><pre>        second =<br>            String.fromInt (Time.toSecond Time.utc time)<br>    in<br>    hour ++ &quot;:&quot; ++ minute ++ &quot;:&quot; ++ second ++ &quot; UTC&quot;</pre><p>Let’s go over the changes one at a time:</p><ul><li>First we import the Html.Events functions, which we will use later.</li><li>In the MODEL section, we are adding a new piece of data to keep track of in the model — the selectedNoteId, which we initialize to 1 to start off.</li><li>In the UPDATE section, we add a <a href="https://guide.elm-lang.org/types/union_types.html">union type</a> to Msg — now it can be a InitializeTimestamps<strong> </strong>or a SelectNote message. So we also add another case to the update function — if the message is SelectNote, we will update the selectedNoteId in the model accordingly.</li><li>In the VIEW section, the viewNoteSelector function has the code onClick (SelectNote note.id), which is binding the DOM click event to trigger the SelectNote message with the note’s id.</li><li>In the VIEW section, the viewNoteSelector function also changed to take in one more argument, the selectedNoteId. This enables us to apply the right CSS active class to the div if the note is the selected note with the code classList [ ( &quot;note-selector&quot;, True ), ( &quot;active&quot;, note.id == selectedNoteId ) ].</li><li>In the VIEW section, we also extracted the code which generates the &lt;div class=&quot;note-editor&quot;&gt; into a separate function called viewNoteEditor. This function uses the model notes and selectedNoteId data to identify which note matches the selectedNoteId. If nothing matches, the note editor will be an empty div. If there is a match, then the note editor will show the editor with the appropriate formatted timestamp and body.</li></ul><p>Here we see another benefit of Elm — it forces you to deal with the case where the selectedNoteId doesn’t match any of the notes. There’s literally no other way to write the code and have it compile. The zero notes scenario wasn’t going to be a problem until we implemented the delete functionality, but it’s reassuring to know that these safety guarantees are built in from the start!</p><h3>Edit the selected note on editor input</h3><p>At this point we can select a note and it highlights accordingly, but the content of the selected note aren’t showing up in the note editor. We also need to be able to update the selected note as the user enters text in the editor. Here’s how the UPDATE and VIEW sections of the code would change in <strong>Notes.elm</strong>:</p><pre>-- UPDATE<br><br><br>type Msg<br>    = InitializeNotesTimestamps Time.Posix<br>    | SelectNote Int<br><strong>    | UpdateSelectedNoteBody String<br>    | UpdateSelectedNoteTimestamp Time.Posix</strong><br><br><br>update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )<br>update msg model =<br>    case msg of<br>        InitializeNotesTimestamps time -&gt;<br>            ( { model<br>                | notes = List.map (\note -&gt; { note | timestamp = Time.posixToMillis time }) model.notes<br>              }<br>            , Cmd.none<br>            )<br><br>        SelectNote id -&gt;<br>            ( { model | selectedNoteId = id }, Cmd.none )<br><br>        <strong>UpdateSelectedNoteBody newText -&gt;<br>            case getSelectedNote model of<br>                Nothing -&gt;<br>                    ( model, Cmd.none )<br><br>                Just selectedNote -&gt;<br>                    let<br>                        updateSelectedNote note =<br>                            if note.id == model.selectedNoteId then<br>                                { note | body = newText }<br><br>                            else<br>                                note<br><br>                        newNotes =<br>                            List.map updateSelectedNote model.notes<br>                    in<br>                    ( { model | notes = newNotes }<br>                    , Task.perform UpdateSelectedNoteTimestamp Time.now<br>                    )<br><br>        UpdateSelectedNoteTimestamp newTime -&gt;<br>            case getSelectedNote model of<br>                Nothing -&gt;<br>                    ( model, Cmd.none )<br><br>                Just selectedNote -&gt;<br>                    let<br>                        updateSelectedNote note =<br>                            if note.id == model.selectedNoteId then<br>                                { note | timestamp = Time.posixToMillis newTime }<br><br>                            else<br>                                note<br><br>                        newNotes =<br>                            List.map updateSelectedNote model.notes<br>                    in<br>                    ( { model | notes = newNotes }, Cmd.none )</strong><br><br><br><br>-- SUBSCRIPTIONS<br><br><br>subscriptions : Model -&gt; Sub Msg<br>subscriptions model =<br>    Sub.none<br><br><br><br>-- VIEW<br><br><br>view : Model -&gt; Html Msg<br>view model =<br>    div [ id &quot;app&quot; ]<br>        [ div [ class &quot;toolbar&quot; ]<br>            [ button [ class &quot;toolbar-button&quot; ] [ text &quot;New&quot; ]<br>            , button [ class &quot;toolbar-button&quot; ] [ text &quot;Delete&quot; ]<br>            , input [ class &quot;toolbar-search&quot;, type_ &quot;text&quot;, placeholder &quot;Search...&quot; ] []<br>            ]<br>        , div [ class &quot;note-container&quot; ]<br>            [ viewNoteSelectors model<br>            , viewNoteEditor model<br>            ]<br>        ]<br><br><br>viewNoteSelectors : Model -&gt; Html Msg<br>viewNoteSelectors model =<br>    div [ class &quot;note-selectors&quot; ]<br>        (model.notes<br>            |&gt; List.sortBy .timestamp<br>            |&gt; List.reverse<br>            |&gt; List.map (\note -&gt; viewNoteSelector note model.selectedNoteId)<br>        )<br><br><br>viewNoteSelector : Note -&gt; Int -&gt; Html Msg<br>viewNoteSelector note selectedNoteId =<br>    div [ classList [ ( &quot;note-selector&quot;, True ), ( &quot;active&quot;, note.id == selectedNoteId ) ], onClick (SelectNote note.id) ]<br>        [ p [ class &quot;note-selector-title&quot; ] [ text (formatTitle note.body) ]<br>        , p [ class &quot;note-selector-timestamp&quot; ] [ text (formatTimestamp note.timestamp) ]<br>        ]<br><br><br>viewNoteEditor : Model -&gt; Html Msg<br>viewNoteEditor model =<br>    case <strong>getSelectedNote</strong> model of<br>        Nothing -&gt;<br>            div [ class &quot;note-editor&quot; ] []<br><br>        Just selectedNote -&gt;<br>            div [ class &quot;note-editor&quot; ]<br>                [ p [ class &quot;note-editor-info&quot; ] [ text (formatTimestamp selectedNote.timestamp) ]<br>                , <strong>textarea [ class &quot;note-editor-input&quot;, onInput UpdateSelectedNoteBody, value selectedNote.body ] []</strong><br>                ]<br><br><br><br>-- HELPERS<br><br><br><strong>getSelectedNote : Model -&gt; Maybe Note<br>getSelectedNote model =<br>    model.notes |&gt; List.filter (\note -&gt; note.id == model.selectedNoteId) |&gt; List.head</strong><br><br><br>formatTitle : String -&gt; String<br>formatTitle body =<br>    let<br>        maxLength =<br>            20<br><br>        length =<br>            String.length body<br>    in<br>    if length &gt; maxLength then<br>        String.left (maxLength - 3) body ++ &quot;...&quot;<br><br>    else if length == 0 then<br>        &quot;New note&quot;<br><br>    else<br>        body<br><br><br>formatTimestamp : Int -&gt; String<br>formatTimestamp timestamp =<br>    let<br>        time =<br>            Time.millisToPosix timestamp<br><br>        hour =<br>            String.fromInt (Time.toHour Time.utc time)<br><br>        minute =<br>            String.fromInt (Time.toMinute Time.utc time)<br><br>        second =<br>            String.fromInt (Time.toSecond Time.utc time)<br>    in<br>    hour ++ &quot;:&quot; ++ minute ++ &quot;:&quot; ++ second ++ &quot; UTC&quot;</pre><p>Let’s examine the changes:</p><ul><li>In the UPDATE section, the Msg now gets two more types — UpdateSelectedNoteBody<strong> </strong>and UpdateSelectedNoteTimestamp. The two are related — if the user enters text in the note editor, it will trigger the UpdateSelectedNoteBody case in the update function, which will update the body of the selected note. When it’s done, it will trigger the Time.now task, which will trigger the UpdateSelectedNoteTimestamp message when it’s ready. When the update function is called with the UpdateSelectedNoteTimestamp case, it will update the selected note with the given time. Note that it has to determine which note matches the selectedNoteId, which is logic we’ve already defined, so I’ve extracted the logic into a function called getSelectedNote which is defined later.</li><li>In the VIEW section, we’ve refactored the viewNoteEditor to use the getSelectedNote function. We are also adding new attributes to the textarea element with the line textarea [ class &quot;note-editor-input&quot;, onInput InputNoteBody, value selectedNote.body ] []. This is binding the DOM input event to trigger the UpdateSelectedNoteBody message, as well as binding the selected note’s body to the textarea value.</li><li>In the HELPERS section, we define the getSelectedNote method, which is just the extracted logic of finding the selected note as before.</li></ul><h3>Create a new note with a button</h3><p>Now let’s implement the ability to create a new note. Clicking on the “New” button should create a new note (new id, no body, current timestamp). The new note should become the currently selected note and appear at the top of the list of note selectors. Here’s how the UPDATE and VIEW sections of the code would change in <strong>Notes.elm</strong>:</p><pre>-- UPDATE<br><br><br>type Msg<br>    = InitializeNotesTimestamps Time.Posix<br>    | SelectNote Int<br>    | UpdateSelectedNoteBody String<br>    | UpdateSelectedNoteTimestamp Time.Posix<br><strong>    | ClickNew<br>    | CreateNote Time.Posix</strong><br><br><br>update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )<br>update msg model =<br>    case msg of<br>        InitializeTimestamps time -&gt;<br>            ( { model<br>                | notes = List.map (\note -&gt; { note | timestamp = time }) model.notes<br>              }<br>            , Cmd.none<br>            )<br><br>        SelectNote id -&gt;<br>            ( { model | selectedNoteId = id }, Cmd.none )<br><br>        InputNoteBody newText -&gt;<br>            ( model, Time.now |&gt; Task.perform (UpdateNote newText) )<br><br>        UpdateNote newText newTimestamp -&gt;<br>            case getSelectedNote model of<br>                Nothing -&gt;<br>                    ( model, Cmd.none )<br><br>                Just selectedNote -&gt;<br>                    let<br>                        updateSelectedNote note =<br>                            if note.id == model.selectedNoteId then<br>                                { note | body = newText, timestamp = newTimestamp }<br>                            else<br>                                note<br>                    in<br>                    ( { model | notes = List.map updateSelectedNote model.notes }<br>                    , Cmd.none<br>                    )<br><br><strong>        ClickNew -&gt;<br>            ( model, Time.now |&gt; Task.perform CreateNote )<br><br>        CreateNote newTimestamp -&gt;<br>            let<br>                newId =<br>                    round (Time.inMilliseconds newTimestamp)<br>            in<br>            ( { model<br>                | notes = [ { id = newId, body = &quot;&quot;, timestamp = newTimestamp } ] ++ model.notes<br>                , selectedNoteId = newId<br>              }<br>            , Cmd.none<br>            )</strong><br><br><br><br>-- VIEW<br><br><br>view : Model -&gt; Html Msg<br>view model =<br>    div [ id &quot;app&quot; ]<br>        [ div [ class &quot;toolbar&quot; ]<br>            [ button [ class &quot;toolbar-button&quot;<strong>, onClick ClickNew</strong> ] [ text &quot;New&quot; ]<br>            , button [ class &quot;toolbar-button&quot; ] [ text &quot;Delete&quot; ]<br>            , input [ class &quot;toolbar-search&quot;, type_ &quot;text&quot;, placeholder &quot;Search...&quot; ] []<br>            ]<br>        , div [ class &quot;note-container&quot; ]<br>            [ viewNoteSelectors model<br>            , viewNoteEditor model<br>            ]<br>        ]</pre><p>Let’s examine the changes:</p><ul><li>In the UPDATE section, the Msg now gets two more types — ClickNew<strong> </strong>and CreateNote. The two are related — if the user clicks the “New” button, it will trigger the ClickNew message, which will in turn trigger the Time.now task which will run the CreateNote message when it’s ready. At that point the CreateNote case in the update function will have the timestamp and can update the model with a new note and selectedNoteId.</li><li>In the VIEW section, the only thing that changes is the “New” button — we add an onClick ClickNew attribute to the button, which binds the DOM on click event to trigger the ClickNew message.</li></ul><p>Here we’re really starting to see the beauty of The Elm Architecture. Features like this are very simple to wire up, without any of the overhead that we saw with different JavaScript component approaches where we had to pass around a lot of info between parents and children.</p><h3>Delete the selected note with a button</h3><p>Wiring up this feature is pretty similar to the new note feature, with a couple of extra considerations. Here’s how the UPDATE and VIEW sections of the code would change in <strong>Notes.elm</strong>:</p><pre>-- UPDATE<br><br><br>type Msg<br>    = InitializeNotesTimestamps Time.Posix<br>    | SelectNote Int<br>    | UpdateSelectedNoteBody String<br>    | UpdateSelectedNoteTimestamp Time.Posix<br>    | ClickNew<br>    | CreateNote Time.Posix<strong><br>    | ClickDelete</strong><br><br><br>update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )<br>update msg model =<br>    case msg of<br>        InitializeNotesTimestamps time -&gt;<br>            ( { model<br>                | notes = List.map (\note -&gt; { note | timestamp = Time.posixToMillis time }) model.notes<br>              }<br>            , Cmd.none<br>            )<br><br>        SelectNote id -&gt;<br>            ( { model | selectedNoteId = id }, Cmd.none )<br><br>        UpdateSelectedNoteBody newText -&gt;<br>            case getSelectedNote model of<br>                Nothing -&gt;<br>                    ( model, Cmd.none )<br><br>                Just selectedNote -&gt;<br>                    let<br>                        updateSelectedNote note =<br>                            if note.id == model.selectedNoteId then<br>                                { note | body = newText }<br><br>                            else<br>                                note<br><br>                        newNotes =<br>                            List.map updateSelectedNote model.notes<br>                    in<br>                    ( { model | notes = newNotes }<br>                    , Task.perform UpdateSelectedNoteTimestamp Time.now<br>                    )<br><br>        UpdateSelectedNoteTimestamp newTime -&gt;<br>            case getSelectedNote model of<br>                Nothing -&gt;<br>                    ( model, Cmd.none )<br><br>                Just selectedNote -&gt;<br>                    let<br>                        updateSelectedNote note =<br>                            if note.id == model.selectedNoteId then<br>                                { note | timestamp = Time.posixToMillis newTime }<br><br>                            else<br>                                note<br><br>                        newNotes =<br>                            List.map updateSelectedNote model.notes<br>                    in<br>                    ( { model | notes = newNotes }, Cmd.none )<br><br>        ClickNew -&gt;<br>            ( model, Task.perform CreateNote Time.now )<br><br>        CreateNote newTime -&gt;<br>            let<br>                newTimestamp =<br>                    Time.posixToMillis newTime<br><br>                newId =<br>                    newTimestamp<br>            in<br>            ( { model<br>                | notes = [ { id = newId, body = &quot;&quot;, timestamp = newTimestamp } ] ++ model.notes<br>                , selectedNoteId = newId<br>              }<br>            , Cmd.none<br>            )<br><strong><br>        ClickDelete -&gt;<br>            let<br>                newNotes =<br>                    List.filter (\note -&gt; note.id /= model.selectedNoteId) model.notes<br><br>                firstVisibleNote =<br>                    newNotes |&gt; sortNotes |&gt; List.head<br>            in<br>            case firstVisibleNote of<br>                Nothing -&gt;<br>                    ( { model | notes = newNotes }, Cmd.none )<br><br>                Just availableNote -&gt;<br>                    ( { model | notes = newNotes, selectedNoteId = availableNote.id }, Cmd.none )</strong><br></pre><pre><br>-- VIEW<br><br><br>view : Model -&gt; Html Msg<br>view model =<br>    div [ id &quot;app&quot; ]<br>        [ div [ class &quot;toolbar&quot; ]<br>            [ button [ class &quot;toolbar-button&quot;, onClick ClickNew ] [ text &quot;New&quot; ]<br>            , button [ class &quot;toolbar-button&quot;<strong>, onClick ClickDelete</strong> ] [ text &quot;Delete&quot; ]<br>            , input [ class &quot;toolbar-search&quot;, type_ &quot;text&quot;, placeholder &quot;Search...&quot; ] []<br>            ]<br>        , div [ class &quot;note-container&quot; ]<br>            [ viewNoteSelectors model<br>            , viewNoteEditor model<br>            ]<br>        ]<br><br><br>viewNoteSelectors : Model -&gt; Html Msg<br>viewNoteSelectors model =<br>    div [ class &quot;note-selectors&quot; ]<br>        (model.notes<br>            <strong>|&gt; sortNotes</strong><br>            |&gt; List.map (\note -&gt; viewNoteSelector note model.selectedNoteId)<br>        )<br><br><br>viewNoteSelector : Note -&gt; Int -&gt; Html Msg<br>viewNoteSelector note selectedNoteId =<br>    div [ classList [ ( &quot;note-selector&quot;, True ), ( &quot;active&quot;, note.id == selectedNoteId ) ], onClick (SelectNote note.id) ]<br>        [ p [ class &quot;note-selector-title&quot; ] [ text (formatTitle note.body) ]<br>        , p [ class &quot;note-selector-timestamp&quot; ] [ text (formatTimestamp note.timestamp) ]<br>        ]<br><br><br>viewNoteEditor : Model -&gt; Html Msg<br>viewNoteEditor model =<br>    case getSelectedNote model of<br>        Nothing -&gt;<br>            div [ class &quot;note-editor&quot; ] []<br><br>        Just selectedNote -&gt;<br>            div [ class &quot;note-editor&quot; ]<br>                [ p [ class &quot;note-editor-info&quot; ] [ text (formatTimestamp selectedNote.timestamp) ]<br>                , textarea [ class &quot;note-editor-input&quot;, onInput UpdateSelectedNoteBody, value selectedNote.body ] []<br>                ]<br><br><br><br>-- HELPERS<br><br><br><strong>sortNotes : List Note -&gt; List Note<br>sortNotes notes =<br>    notes |&gt; List.sortBy .timestamp |&gt; List.reverse</strong></pre><p>Let’s examine the changes:</p><ul><li>In the UPDATE section, the Msg now one more type— ClickDelete, which will be wired up to run whenever the user clicks the “Delete” button. The update function with the ClickDelete case has to both delete the selected note (using List.filter) and select a new note, which should be the top of the sorted notes. Here I extracted the logic to sort the notes into a separate sortNotes function (defined later) to keep the code DRY.</li><li>In the VIEW section, the only significant change is the “Delete” button — we add an onClick ClickDelete attribute to the button, which binds the DOM on click event to trigger the ClickDelete message. The only other change is refactoring the viewNoteSelectors function to use the sortNotes function to keep the code DRY.</li></ul><p>Again, this change was a lot simpler to make compared to our other approaches, particularly in this case because Elm forced us to handle the case of no notes earlier!</p><h3>Filter notes on search input</h3><p>The final feature is to be able to search notes immediately as you type in the search input. Since this is the last feature, let’s look at the code in <strong>Notes.elm</strong> in its entirety:</p><pre>module Notes exposing (main)<br><br>import Browser<br>import Html exposing (..)<br>import Html.Attributes exposing (..)<br>import Html.Events exposing (..)<br>import Task<br>import Time<br><br><br>main =<br>    Browser.element<br>        { init = init<br>        , view = view<br>        , update = update<br>        , subscriptions = subscriptions<br>        }<br><br><br><br>-- MODEL<br><br><br>type alias Note =<br>    { id : Int, body : String, timestamp : Int }<br><br><br>type alias Model =<br>    { notes : List Note, selectedNoteId : Int<strong>, searchNoteText : String</strong> }<br><br><br>init : () -&gt; ( Model, Cmd Msg )<br>init _ =<br>    ( { notes =<br>            [ { id = 1, body = &quot;First note...&quot;, timestamp = 0 }<br>            , { id = 2, body = &quot;Second note...&quot;, timestamp = 0 }<br>            , { id = 3, body = &quot;Third note...&quot;, timestamp = 0 }<br>            ]<br>      , selectedNoteId = 1<br>      <strong>, searchNoteText = &quot;&quot;</strong><br>      }<br>    , Task.perform InitializeNotesTimestamps Time.now<br>    )<br><br><br><br>-- UPDATE<br><br><br>type Msg<br>    = InitializeNotesTimestamps Time.Posix<br>    | SelectNote Int<br>    | UpdateSelectedNoteBody String<br>    | UpdateSelectedNoteTimestamp Time.Posix<br>    | ClickNew<br>    | CreateNote Time.Posix<br>    | ClickDelete<br>    <strong>| InputSearch String</strong><br><br><br>update : Msg -&gt; Model -&gt; ( Model, Cmd Msg )<br>update msg model =<br>    case msg of<br>        InitializeNotesTimestamps time -&gt;<br>            ( { model<br>                | notes = List.map (\note -&gt; { note | timestamp = Time.posixToMillis time }) model.notes<br>              }<br>            , Cmd.none<br>            )<br><br>        SelectNote id -&gt;<br>            ( { model | selectedNoteId = id }, Cmd.none )<br><br>        UpdateSelectedNoteBody newText -&gt;<br>            case getSelectedNote model of<br>                Nothing -&gt;<br>                    ( model, Cmd.none )<br><br>                Just selectedNote -&gt;<br>                    let<br>                        updateSelectedNote note =<br>                            if note.id == model.selectedNoteId then<br>                                { note | body = newText }<br><br>                            else<br>                                note<br><br>                        newNotes =<br>                            List.map updateSelectedNote model.notes<br>                    in<br>                    ( { model | notes = newNotes }<br>                    , Task.perform UpdateSelectedNoteTimestamp Time.now<br>                    )<br><br>        UpdateSelectedNoteTimestamp newTime -&gt;<br>            case getSelectedNote model of<br>                Nothing -&gt;<br>                    ( model, Cmd.none )<br><br>                Just selectedNote -&gt;<br>                    let<br>                        updateSelectedNote note =<br>                            if note.id == model.selectedNoteId then<br>                                { note | timestamp = Time.posixToMillis newTime }<br><br>                            else<br>                                note<br><br>                        newNotes =<br>                            List.map updateSelectedNote model.notes<br>                    in<br>                    ( { model | notes = newNotes }, Cmd.none )<br><br>        ClickNew -&gt;<br>            ( model, Task.perform CreateNote Time.now )<br><br>        CreateNote newTime -&gt;<br>            let<br>                newTimestamp =<br>                    Time.posixToMillis newTime<br><br>                newId =<br>                    newTimestamp<br>            in<br>            ( { model<br>                | notes = [ { id = newId, body = &quot;&quot;, timestamp = newTimestamp } ] ++ model.notes<br>                , selectedNoteId = newId<br>              }<br>            , Cmd.none<br>            )<br><br>        ClickDelete -&gt;<br>            let<br>                newNotes =<br>                    List.filter (\note -&gt; note.id /= model.selectedNoteId) model.notes<br><br>                firstVisibleNote =<br>                    <strong>getFirstVisibleNote newNotes model.searchNoteText</strong><br>            in<br>            case firstVisibleNote of<br>                Nothing -&gt;<br>                    ( { model | notes = newNotes }, Cmd.none )<br><br>                Just availableNote -&gt;<br>                    ( { model | notes = newNotes, selectedNoteId = availableNote.id }, Cmd.none )<br><br><strong>        InputSearch searchNoteText -&gt;<br>            let<br>                firstVisibleNote =<br>                    getFirstVisibleNote model.notes searchNoteText<br>            in<br>            case firstVisibleNote of<br>                Nothing -&gt;<br>                    ( { model | searchNoteText = searchNoteText, selectedNoteId = -1 }, Cmd.none )<br><br>                Just availableNote -&gt;<br>                    ( { model | searchNoteText = searchNoteText, selectedNoteId = availableNote.id }, Cmd.none )</strong><br><br><br><br>-- SUBSCRIPTIONS<br><br><br>subscriptions : Model -&gt; Sub Msg<br>subscriptions model =<br>    Sub.none<br><br><br><br>-- VIEW<br><br><br>view : Model -&gt; Html Msg<br>view model =<br>    div [ id &quot;app&quot; ]<br>        [ div [ class &quot;toolbar&quot; ]<br>            [ button [ class &quot;toolbar-button&quot;, onClick ClickNew ] [ text &quot;New&quot; ]<br>            , button [ class &quot;toolbar-button&quot;, onClick ClickDelete ] [ text &quot;Delete&quot; ]<br>            , input [ class &quot;toolbar-search&quot;, type_ &quot;text&quot;, placeholder &quot;Search...&quot;<strong>, onInput InputSearch</strong> ] []<br>            ]<br>        , div [ class &quot;note-container&quot; ]<br>            [ viewNoteSelectors model<br>            , viewNoteEditor model<br>            ]<br>        ]<br><br><br>viewNoteSelectors : Model -&gt; Html Msg<br>viewNoteSelectors model =<br>    div [ class &quot;note-selectors&quot; ]<br>        (model.notes<br>            <strong>|&gt; transformNotes model.searchNoteText</strong><br>            |&gt; List.map (\note -&gt; viewNoteSelector note model.selectedNoteId)<br>        )<br><br><br>viewNoteSelector : Note -&gt; Int -&gt; Html Msg<br>viewNoteSelector note selectedNoteId =<br>    div [ classList [ ( &quot;note-selector&quot;, True ), ( &quot;active&quot;, note.id == selectedNoteId ) ], onClick (SelectNote note.id) ]<br>        [ p [ class &quot;note-selector-title&quot; ] [ text (formatTitle note.body) ]<br>        , p [ class &quot;note-selector-timestamp&quot; ] [ text (formatTimestamp note.timestamp) ]<br>        ]<br><br><br>viewNoteEditor : Model -&gt; Html Msg<br>viewNoteEditor model =<br>    case getSelectedNote model of<br>        Nothing -&gt;<br>            div [ class &quot;note-editor&quot; ] []<br><br>        Just selectedNote -&gt;<br>            div [ class &quot;note-editor&quot; ]<br>                [ p [ class &quot;note-editor-info&quot; ] [ text (formatTimestamp selectedNote.timestamp) ]<br>                , textarea [ class &quot;note-editor-input&quot;, onInput UpdateSelectedNoteBody, value selectedNote.body ] []<br>                ]<br><br><br><br>-- HELPERS<br><br><br><strong>getFirstVisibleNote : List Note -&gt; String -&gt; Maybe Note<br>getFirstVisibleNote notes searchText =<br>    notes<br>        |&gt; transformNotes searchText<br>        |&gt; List.head</strong><br><br><br><strong>transformNotes : String -&gt; List Note -&gt; List Note<br>transformNotes searchNoteText notes =<br>    notes<br>        |&gt; List.filter (\note -&gt; String.contains (String.toLower searchNoteText) (String.toLower note.body))<br>        |&gt; List.sortBy .timestamp<br>        |&gt; List.reverse</strong><br><br><br>getSelectedNote : Model -&gt; Maybe Note<br>getSelectedNote model =<br>    model.notes<br>        <strong>|&gt; transformNotes model.searchNoteText</strong><br>        |&gt; List.filter (\note -&gt; note.id == model.selectedNoteId)<br>        |&gt; List.head<br><br><br>formatTitle : String -&gt; String<br>formatTitle body =<br>    let<br>        maxLength =<br>            20<br><br>        length =<br>            String.length body<br>    in<br>    if length &gt; maxLength then<br>        String.left (maxLength - 3) body ++ &quot;...&quot;<br><br>    else if length == 0 then<br>        &quot;New note&quot;<br><br>    else<br>        body<br><br><br>formatTimestamp : Int -&gt; String<br>formatTimestamp timestamp =<br>    let<br>        time =<br>            Time.millisToPosix timestamp<br><br>        hour =<br>            String.fromInt (Time.toHour Time.utc time)<br><br>        minute =<br>            String.fromInt (Time.toMinute Time.utc time)<br><br>        second =<br>            String.fromInt (Time.toSecond Time.utc time)<br>    in<br>    hour ++ &quot;:&quot; ++ minute ++ &quot;:&quot; ++ second ++ &quot; UTC&quot;</pre><p>Let’s examine the changes:</p><ul><li>In the MODEL section, we are adding a new piece of data to keep track of in the model — the searchNoteText, which we initialize to an empty string.</li><li>In the UPDATE section, the Msg now one more type— InputSearch, which will be wired up to run whenever the user enters text in the search input. The update function with the InputSearch case has to filter notes that match the searchNoteText as well as select the first visible note. Since I already had logic in the ClickDelete case for getting the first visible note, I extracted it into a method called getFirstVisibleNote to be DRY and use it in both the ClickDelete and InputSearch cases.</li><li>In the VIEW section, we add an onInput InputSearch attribute to the button, which binds the DOM input event to trigger the InputSearch message. We also change the viewNoteSelectors function to not use sortNotes function and use transformNotes instead, which both filters the list of notes as well as sorts them.</li><li>After the VIEW section, we define the new helper functions getFirstVisibleNote as well as transformNotes (which used to be sortNotes).</li></ul><p>There may be some further refactoring that can be done to make the use of transformNotes more clear. The beauty of Elm is that the compiler works well enough to give you confidence when making such refactors — if it compiles, it works.</p><p>The only file we’ve been changing this whole time is <strong>Notes.elm</strong>, which you see above. If you’re curious, you can check out the entire project at this <a href="https://github.com/peterxjang/notes-app-elm/tree/80457fb818d5af671fe23740d5f4bc2c418d1e42">repository</a>. (Side note — if you’re not a fan of having all the code in one file, there are definite patterns you can use to organize your code, but they won’t necessarily be the same patterns you may be used to in JavaScript. Check out <a href="https://twitter.com/rtfeldman">Richard Feldman</a>’s talk on <a href="https://www.youtube.com/watch?v=DoA4Txr4GUs">scaling Elm apps</a> to get a sense of what I mean!)</p><h3>Conclusion</h3><p>Working with Elm is a pretty large departure from every other framework we looked at. It’s a purely functional language, which means it provides certain guarantees that a JavaScript framework simply can’t. React uses functional patterns like immutability, but since JavaScript is not an immutable language you’re required to work harder to avoid breaking the code. Programming in React often comes with using libraries like Immutable.js for immutable types and Flow for static type checking, essentially bolting on language features onto JavaScript that it doesn’t naturally support. Using Elm makes the whole process much smoother to work with.</p><p>The downsides to Elm are the learning curve and dealing with the outside world. For the learning curve, Elm works hard to utilize difficult functional concepts in a way that’s easy to get started. The best way to learn Elm <strong>isn’t</strong> to try and master the language — it’s to start building apps and learn as you go. The other downside is dealing with the outside world — as we saw, something as simple as getting a timestamp was fairly involved to wire up in Elm. Working with HTTP requests, JSON responses, WYSIWYG editors — these all require a lot more work compared to quick and dirty (but dangerous) JavaScript approaches.</p><p>In the <a href="https://medium.com/@peterxjang/comparing-frontend-approaches-part-7-final-thoughts-69cdba516f86">last part</a> of this series, I’ll wrap up with some final thoughts about each frontend approach and how you might choose the right tool for the job. Stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=578714526164" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/comparing-frontend-frameworks-part-6-elm-578714526164">Comparing Frontend Approaches Part 6: Elm</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Comparing Frontend Approaches Part 5: React]]></title>
            <link>https://medium.com/actualize-network/comparing-frontend-frameworks-part-5-react-b51fd7d075fe?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/b51fd7d075fe</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Sat, 13 Jan 2018 23:14:20 GMT</pubDate>
            <atom:updated>2019-02-24T19:10:36.071Z</atom:updated>
            <content:encoded><![CDATA[<h4>A look at jQuery, Vue.js, React, and Elm</h4><ul><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">Part 1: Introduction</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">Part 2: jQuery</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">Part 3: Vue.js</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Part 4: Vue.js with components</a></li><li><strong>Part 5: React</strong></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-6-elm-578714526164">Part 6: Elm</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-approaches-part-7-final-thoughts-69cdba516f86">Part 7: Final thoughts</a></li></ul><p>In this part we will be implementing the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">web based clone</a> of the <a href="https://support.apple.com/kb/PH22608?locale=en_US">Mac Notes app</a> using <a href="https://facebook.github.io/react/">React</a>. React is a JavaScript framework that was created around 2011 and open-sourced in 2013. It currently has won over developer mindshare in a big way and is the most popular framework in 2017 by various metrics (over 75,000 stars on GitHub).</p><p>React pioneered the virtual DOM approach (at least in JavaScript frameworks), where the HTML is completely generated through JavaScript instead of a template. It also was one of the first frontend frameworks to push for a completely component centered approach. Unlike <a href="https://vuejs.org/">Vue.js</a>, which bills itself as a progressive framework (i.e., you don’t need to start with components, you can work your way up there), React is a bit more insistent (the <a href="https://facebook.github.io/react/docs/thinking-in-react.html#step-1-break-the-ui-into-a-component-hierarchy">very first step</a> they recommend is to break the UI into components). Let’s see what it looks like in practice!</p><blockquote>Note — this isn’t meant to be a tutorial for people completely new to React. In particular, React uses a lot of new JavaScript ES2015+ features, which I won’t be covering in full detail. You can check out <a href="https://babeljs.io/learn-es2015/">this resource</a> first if you’d like an overview of these new features before reading this post.</blockquote><h3>Installation</h3><p>Like <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Vue.js with components</a>, getting started with React can be a bit tricky, since writing code with components involves bundling code in separate files, requiring a build step. Fortunately, like Vue.js, React offers an official command line tool to scaffold your projects, called <a href="https://github.com/facebookincubator/create-react-app">Create React App</a>. If you don’t already have it installed, you can install it with the command:</p><pre>$ npm install -g create-react-app</pre><p>Once you have it installed, you can navigate to the directory you want to create your project in and run:</p><pre>$ create-react-app my-app</pre><p>You should replace my-app with whatever name you want your particular project to have. This will take a little while to create files and folders and download the necessary packages from npm. In the end you will end up a new folder called my-app with the following project structure:</p><pre>my-app<br>├── README.md<br>├── node_modules<br>├── package.json<br>├── .gitignore<br>├── public<br>│   └── favicon.ico<br>│   └── index.html<br>│   └── manifest.json<br>└── src<br>    └── App.css<br>    └── App.js<br>    └── App.test.js<br>    └── index.css<br>    └── index.js<br>    └── logo.svg<br>    └── registerServiceWorker.js</pre><p>Again, even though there’s a lot of files, you don’t need to worry about most of them for right now — pretty much all of our work will be in the <strong>src</strong> directory. At this point you already have a hello world app built for you, which you can see by entering the following commands:</p><pre>$ cd my-app<br>$ npm start</pre><p>The last command will run a dev server and automatically open up your browser to localhost:3000, where you should see something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T__IgXdlzLHGwJ2ftWBpVg.png" /></figure><p>This is pretty cool, and what’s cooler is that you have a hot-reloading environment already set up for you. What that means is that if you make changes to your code, the browser at localhost:3000 will automatically refresh. As it says, the place to start making changes is <strong>src/App.js</strong>, which right now looks like this:</p><pre>import React, { Component } from &#39;react&#39;;<br>import logo from &#39;./logo.svg&#39;;<br>import &#39;./App.css&#39;;</pre><pre>class App extends Component {<br>  render() {<br>    return (<br>      &lt;div className=&quot;App&quot;&gt;<br>        &lt;header className=&quot;App-header&quot;&gt;<br>          &lt;img src={logo} className=&quot;App-logo&quot; alt=&quot;logo&quot; /&gt;<br>          &lt;h1 className=&quot;App-title&quot;&gt;Welcome to React&lt;/h1&gt;<br>        &lt;/header&gt;<br>        &lt;p className=&quot;App-intro&quot;&gt;<br>          To get started, edit &lt;code&gt;src/App.js&lt;/code&gt; and save to reload.<br>        &lt;/p&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}</pre><pre>export default App;</pre><p>This is a React component — it makes heavy use of both <a href="https://babeljs.io/learn-es2015/">ES2015 JavaScript features</a> (imports, destructuring, classes) as well as <a href="https://facebook.github.io/react/docs/jsx-in-depth.html">JSX</a>. JSX is syntax which allows you to write what looks like HTML in the middle of your JavaScript (it’s not actually HTML, it gets converted into JavaScript statements during a build step). So your HTML template is defined in your JavaScript, there are no separate files for your HTML. In this example, the CSS is defined in the separate <strong>App.css</strong> file and is imported into the JavaScript as well. This means we can install our notes template by changing <strong>src/App.js </strong>as follows:</p><pre>import React, { Component } from &#39;react&#39;;<br>import &#39;./App.css&#39;;<br><br>class App extends Component {<br>  render() {<br>    return (<br>      &lt;div id=&quot;app&quot;&gt;<br>        &lt;div className=&quot;toolbar&quot;&gt;<br>          &lt;button className=&quot;toolbar-button&quot;&gt;New&lt;/button&gt;<br>          &lt;button className=&quot;toolbar-button&quot;&gt;Delete&lt;/button&gt;<br>          &lt;input className=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot;&gt;&lt;/input&gt;<br>        &lt;/div&gt;<br>        &lt;div className=&quot;note-container&quot;&gt;<br>          &lt;div className=&quot;note-selectors&quot;&gt;<br>            &lt;div className=&quot;note-selector active&quot;&gt;<br>              &lt;p className=&quot;note-selector-title&quot;&gt;First note...&lt;/p&gt;<br>              &lt;p className=&quot;note-selector-timestamp&quot;&gt;Timestamp here...&lt;/p&gt;<br>            &lt;/div&gt;<br>            &lt;div className=&quot;note-selector&quot;&gt;<br>              &lt;p className=&quot;note-selector-title&quot;&gt;Second note...&lt;/p&gt;<br>              &lt;p className=&quot;note-selector-timestamp&quot;&gt;Timestamp here...&lt;/p&gt;<br>            &lt;/div&gt;<br>            &lt;div className=&quot;note-selector&quot;&gt;<br>              &lt;p className=&quot;note-selector-title&quot;&gt;Third note...&lt;/p&gt;<br>              &lt;p className=&quot;note-selector-timestamp&quot;&gt;Timestamp here...&lt;/p&gt;<br>            &lt;/div&gt;<br>          &lt;/div&gt;<br>          &lt;div className=&quot;note-editor&quot;&gt;<br>            &lt;p className=&quot;note-editor-info&quot;&gt;Timestamp here...&lt;/p&gt;<br>            &lt;textarea className=&quot;note-editor-input&quot;&gt;&lt;/textarea&gt;<br>          &lt;/div&gt;<br>        &lt;/div&gt;<br>      &lt;/div&gt;      <br>    );<br>  }<br>}<br><br>export default App;</pre><p>Note that I had to convert the HTML to JSX, which is mostly the same but with minor differences to keep the syntax compatible with JavaScript. In this case, the HTML class attribute had to be renamed to className (since class is a reserved word in JavaScript). Once the JavaScript is set up, we can add the note template’s CSS in the <strong>App.css </strong>file as follows:</p><pre>/* RESET */<br>* {<br>  margin: 0;<br>  padding: 0;<br>  border: 0;<br>  outline: none;<br>  box-sizing: border-box;<br>}<br><br>/* LAYOUT */<br>#app {<br>  display: flex;<br>  flex-direction: column;<br>  min-height: 100vh;<br>}<br>.toolbar {<br>  padding: 0.5em;<br>}<br>.toolbar-button, .toolbar-search {<br>  padding: inherit;<br>  border-radius: 0.3em;<br>}<br>.toolbar-search {<br>  float: right;<br>}<br>.note-container {<br>  display: flex;<br>  flex: 1;<br>}<br>.note-selectors {<br>  flex: 0 0 13em;<br>}<br>.note-selector {<br>  padding: 1em;<br>}<br>.note-selector p {<br>  margin: 0;<br>}<br>.note-editor {<br>  display: flex;<br>  flex: 1;<br>  flex-direction: column;<br>}<br>.note-editor-info {<br>  padding: 0.5em;<br>  text-align: center;<br>}<br>.note-editor-input {<br>  display: flex;<br>  flex: 1;<br>  width: 100%;<br>  padding: 0 2em 0 2em;<br>}<br><br>/* COLORS */<br>* {<br>  color: #454545;<br>  background-color: #FAFAF8;<br>}<br>.toolbar {<br>  background-color: #DCDADC;<br>}<br>.toolbar-button {<br>  background-color: #FFFFFF;<br>}<br>.toolbar-button:active {<br>  background-color: #AAAAAA;<br>}<br>.note-selectors {<br>  border-right: 1px solid #DCDADC;<br>}<br>.note-selector {<br>  border-bottom: 1px solid #DCDADC;<br>}<br>.note-selector.active {<br>  background-color: #FCE18D;<br>}<br>.note-selector-title {<br>  background-color: inherit;<br>}<br>.note-selector-timestamp {<br>  color: #626262;<br>  background-color: inherit;<br>}<br>.note-editor-info {<br>  color: #DCDADC;<br>}<br><br>/* TYPOGRAPHY */<br>body {<br>  font-family: sans-serif;<br>}<br>.note-selector-title {<br>  font-weight: bold;<br>}<br>.note-selector-timestamp {<br>  font-size: 0.7em;<br>}<br>.note-editor, .note-editor-input {<br>  font-size: 0.9em;<br>}</pre><p>If you look at your browser, you should see our familiar notes app starting point!</p><h3>Refactor into components</h3><p>The next step is to break this <strong>src/App.js</strong> file into components. Here is a diagram of the different components we can break this app into:</p><pre>&lt;App&gt;<br>├── &lt;Toolbar&gt;<br>└── &lt;NoteContainer&gt;<br>    ├── &lt;NoteSelectors&gt;<br>    │   └── &lt;NoteSelector&gt;<br>    │   └── &lt;NoteSelector&gt;<br>    │   └── &lt;NoteSelector&gt;<br>    └── &lt;NoteEditor&gt;</pre><p>You could of course break it down even further (with individual button components for the toolbar for example), but this is a good starting point. Using this structure, the <strong>src/App.js</strong> file would now look like:</p><pre>import React, { Component } from &#39;react&#39;;<br><strong>import Toolbar from &#39;./components/Toolbar&#39;<br>import NoteContainer from &#39;./components/NoteContainer&#39;</strong><br>import &#39;./App.css&#39;;<br><br>class App extends Component {<br>  render() {<br>    return (<br>      &lt;div id=&quot;app&quot;&gt;<br><strong>        &lt;Toolbar /&gt;<br>        &lt;NoteContainer /&gt;</strong><br>      &lt;/div&gt;      <br>    );<br>  }<br>}<br><br>export default App;</pre><p>The template essentially only contains the &lt;Toolbar /&gt; and &lt;NoteContainer /&gt;custom component tags, which are imported above (note the self closing tags, which is the recommended style for components without any content). Let’s take a look at one of the sub-components, <strong>src/components/Toolbar.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br><br>class Toolbar extends Component {<br>  render() {<br>    return (<br>      &lt;div className=&quot;toolbar&quot;&gt;<br>        &lt;button className=&quot;toolbar-button&quot;&gt;New&lt;/button&gt;<br>        &lt;button className=&quot;toolbar-button&quot;&gt;Delete&lt;/button&gt;<br>        &lt;input className=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot;&gt;&lt;/input&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default Toolbar;</pre><p>As you can see, it’s the same as before — you create a new class which extends the React Component class, and you write a render function which defines the JSX template.</p><p>So breaking things into components is a pretty straightforward task. The harder part comes when dealing with data and events. If you want to see what the rest of the components look like right now (they pretty much follow the same pattern), you can check them out in full detail <a href="https://github.com/peterxjang/notes-app-react/tree/8b1b7c37e05b69d4e80d6ba67118ae77ec3de82b/src/components">here</a>.</p><ul><li><a href="https://github.com/peterxjang/notes-app-react/blob/8b1b7c37e05b69d4e80d6ba67118ae77ec3de82b/src/components/Toolbar.js">Toolbar.js</a></li><li><a href="https://github.com/peterxjang/notes-app-react/blob/8b1b7c37e05b69d4e80d6ba67118ae77ec3de82b/src/components/NoteContainer.js">NoteContainer.js</a></li><li><a href="https://github.com/peterxjang/notes-app-react/blob/8b1b7c37e05b69d4e80d6ba67118ae77ec3de82b/src/components/NoteSelectors.js">NoteSelectors.js</a></li><li><a href="https://github.com/peterxjang/notes-app-react/blob/8b1b7c37e05b69d4e80d6ba67118ae77ec3de82b/src/components/NoteSelector.js">NoteSelector.js</a></li><li><a href="https://github.com/peterxjang/notes-app-react/blob/8b1b7c37e05b69d4e80d6ba67118ae77ec3de82b/src/components/NoteEditor.js">NoteEditor.js</a></li></ul><h3>Display note titles from an array of notes</h3><p>Now that things are broken into components, we have to make some choices when it comes to storing and passing data. For the array of notes, it makes sense for the main &lt;App&gt; component to keep track of the notes and pass it down to its children. This would make the code in <strong>src/App.js</strong> look like:</p><pre>import React, { Component } from &#39;react&#39;;<br>import Toolbar from &#39;./components/Toolbar&#39;<br>import NoteContainer from &#39;./components/NoteContainer&#39;<br>import &#39;./App.css&#39;;<br><br>class App extends Component {<br><strong>  constructor(props) {<br>    super(props);<br>    this.state = {<br>      notes: [<br>        {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>        {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>        {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>      ]<br>    }<br>  }<br><br></strong>  render() {<br>    return (<br>      &lt;div id=&quot;app&quot;&gt;<br>        &lt;Toolbar /&gt;<br>        &lt;NoteContainer <strong>notes={this.state.notes} </strong>/&gt;<br>      &lt;/div&gt;      <br>    );<br>  }<br>}<br><br>export default App;</pre><p>Here we have to make a custom constructor function for the class, which first runs the Component constructor with super(props);, then defines the custom state for the instance (which is equivalent to Vue.js’ data option). Once the state is stored upon initialization, we can pass it to the &lt;NoteContainer&gt; component with the line:</p><pre>        &lt;NoteContainer <strong>notes={this.state.notes} </strong>/&gt;</pre><p>In JSX, you can write any JavaScript you want in { } (as opposed to {{ }} in Vue.js). Here we’re taking the notes initialized in the component’s state and binding it to the child &lt;NoteContainer&gt; component. The &lt;NoteContainer&gt; component doesn’t directly use the notes data — instead it passes it down to the next child component. The code in <strong>src/components/NoteContainer.js</strong> looks like:</p><pre>import React, { Component } from &#39;react&#39;;<br>import NoteSelectors from &#39;./NoteSelectors&#39;;<br>import NoteEditor from &#39;./NoteEditor&#39;;<br><br>class NoteContainer extends Component {<br>  render() {<br>    return (<br>      &lt;div className=&quot;note-container&quot;&gt;<br>        &lt;NoteSelectors <strong>notes={this.props.notes}</strong> /&gt;<br>        &lt;NoteEditor /&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteContainer;</pre><p>Note the change in terminology — this time notes is referred to using props, not state. It’s worth going over in more detail here:</p><ul><li>The parent &lt;App&gt; component contains the note data, stored in its state. It has the ability to make changes to its own state, as well as pass any part of it down to its child components.</li><li>Any data a child component receives from its parent component is stored in props. In this case the child &lt;NoteContainer&gt; component receives the notes data in its props. A child component is not allowed to make changes to the props, since the data belongs to the parent component.</li></ul><p>This means in the child &lt;NoteContainer&gt; component, the line</p><pre>        &lt;NoteSelectors notes={this.props.notes} /&gt; </pre><p>is taking the this.props.notes data it received from its parent and passing it to the child &lt;NoteSelectors&gt; component as new props with a key of notes.</p><p>Finally, we get to &lt;NoteSelectors&gt; component, which does actually use the notes. This is what the code in <strong>src/components/NoteSelectors.js</strong> looks like:</p><pre>import React, { Component } from &#39;react&#39;;<br>import NoteSelector from &#39;./NoteSelector&#39;;<br><br>class NoteSelectors extends Component {<br>  render() {<br><strong>    const noteSelectors = this.props.notes.map(note =&gt; <br>      &lt;NoteSelector<br>        key={note.id}<br>        body={note.body}<br>        timestamp={note.timestamp}<br>      /&gt;<br>    );<br><br></strong>    return (<br>      &lt;div className=&quot;note-selectors&quot;&gt;<br>        <strong>{noteSelectors}</strong><br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteSelectors;</pre><p>React doesn’t provide a special mechanism to loop through data — instead we’re using the built-in array map function to create an array of &lt;NoteSelector&gt; components, stored in the variable noteSelectors and rendered in the JSX. Finally, the &lt;NoteSelector&gt; component renders the data bound to its props, as you can see in <strong>src/components/NoteSelector.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br><br>class NoteSelector extends Component {<br>  render() {<br>    return (<br>      &lt;div className=&quot;note-selector&quot;&gt;<br>        &lt;p className=&quot;note-selector-title&quot;&gt;<br>          <strong>{this.props.body}<br>        </strong>&lt;/p&gt;<br>        &lt;p className=&quot;note-selector-timestamp&quot;&gt;<br>          <strong>{this.props.timestamp}<br>        </strong>&lt;/p&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteSelector;</pre><p>For now, I’m using the note’s body and raw timestamp as placeholders, we’ll look at formatting them next.</p><h3>Use functions to sort and format notes</h3><p>Unlike Vue.js, React doesn’t provide special mechanisms for things like computed properties and filters. Instead, you simply write functions as needed. This is what the <strong>src/components/NoteSelector.js</strong> code looks like with text formatting:</p><pre>import React, { Component } from &#39;react&#39;;<br><strong><br>function formatTitle(body) {<br>  var maxLength = 20;<br>  if (body.length &gt; maxLength) {<br>    return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>  } else if (body.length === 0) {<br>    return &quot;New note&quot;;<br>  } else {<br>    return body;<br>  }<br>}<br><br>function formatTimestamp(timestamp) {<br>  return new Date(timestamp).toUTCString();<br>}</strong><br><br>class NoteSelector extends Component {<br>  render() {<br>    return (<br>      &lt;div className=&quot;note-selector&quot;&gt;<br>        &lt;p className=&quot;note-selector-title&quot;&gt;<br>          <strong>{formatTitle(this.props.body)}</strong><br>        &lt;/p&gt;<br>        &lt;p className=&quot;note-selector-timestamp&quot;&gt;<br>          <strong>{formatTimestamp(this.props.timestamp)}</strong><br>        &lt;/p&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteSelector;</pre><p>Similarly, this is what the <strong>src/components/NoteSelectors.js</strong> code looks like to sort the child &lt;NoteSelector&gt; components by timestamps:</p><pre>import React, { Component } from &#39;react&#39;;<br>import NoteSelector from &#39;./NoteSelector&#39;;<br><br>class NoteSelectors extends Component {<br>  render() {<br><strong>    const sortedNotes = this.props.notes.slice().sort((a, b) =&gt; <br>      b.timestamp - a.timestamp<br>    )</strong><br>    const noteSelectors = <strong>sortedNotes</strong>.map(note =&gt; <br>      &lt;NoteSelector<br>        key={note.id}<br>        body={note.body}<br>        timestamp={note.timestamp}<br>      /&gt;<br>    );<br><br>    return (<br>      &lt;div className=&quot;note-selectors&quot;&gt;<br>        {noteSelectors}<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteSelectors;</pre><p>This is one of the promises of React as a framework — it tries to keep its API to a minimum, relying on vanilla JavaScript whenever possible to implement features.</p><h3>Select a note on title click</h3><p>Now let’s implement the ability to actually select notes. Clicking on a note title should both highlight the selected note on the left as well as display the contents in the editor on the right. First we’ll change the template in <strong>src/App.js</strong> as follows:</p><pre>import React, { Component } from &#39;react&#39;;<br>import Toolbar from &#39;./components/Toolbar&#39;<br>import NoteContainer from &#39;./components/NoteContainer&#39;<br>import &#39;./App.css&#39;;<br><br>class App extends Component {<br>  constructor(props) {<br>    super(props);<br>    this.state = {<br>      notes: [<br>        {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>        {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>        {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>      ],<br>      <strong>selectedNoteId: 1</strong><br>    }<br>  }<br><br><strong>  handleClickNote = (id) =&gt; {<br>    this.setState({selectedNoteId: id});<br>  }</strong><br><br>  render() {<br>    return (<br>      &lt;div id=&quot;app&quot;&gt;<br>        &lt;Toolbar /&gt;<br>        &lt;NoteContainer<br>          notes={this.state.notes}<br>          <strong>selectedNoteId={this.state.selectedNoteId}</strong><br>          <strong>onClickNote={this.handleClickNote}</strong><br>        /&gt;<br>      &lt;/div&gt;      <br>    );<br>  }<br>}<br><br>export default App;</pre><p>Let’s go over these changes one at a time:</p><ul><li>We are adding selectedNoteId to the state to keep track of which note was selected. This is different than Vue.js, where we kept track of the note itself instead of just the id. This works better with the way React handles changes to the state, which we’ll see next.</li><li>We are defining a new handleClickNote function which takes an id and updates the state using React’s setState method. This is an important part of React — you must use this setState method to change any state, without it React won’t be able to properly update the DOM when state changes. (Note the weird way I’m defining the handleClickNote method in the class — <a href="https://babeljs.io/docs/plugins/transform-class-properties/">this syntax</a> helps properly bind the right this to the method in an efficient way. You can read more about it and other approaches <a href="https://facebook.github.io/react/docs/handling-events.html">here</a>).</li><li>We are binding a new prop to the child &lt;NoteContainer&gt; component called selectedNoteId, which is set to the current component’s state’s selectedNoteId.</li><li>We are binding another new prop to the child &lt;NoteContainer&gt; component called onClickNote, which is set to the current component’s handleClickNote method we defined earlier. This is also different from Vue.js — there is no special notion of event emitters and listeners in React, you simply pass functions as part of the regular props which the child will call when appropriate.</li></ul><p>Note the naming convention here — the actual event handler is named handleClickNote, the event handler as passed through in props is named onClickNote. This makes it easier to keep track of where event handlers are coming from, which we can see more clearly as we follow the props into the child components. The code in <strong>src/components/NoteContainer.js</strong> looks like:</p><pre>import React, { Component } from &#39;react&#39;;<br>import NoteSelectors from &#39;./NoteSelectors&#39;;<br>import NoteEditor from &#39;./NoteEditor&#39;;<br><br>class NoteContainer extends Component {<br>  render() {<br>    return (<br>      &lt;div className=&quot;note-container&quot;&gt;<br>        &lt;NoteSelectors<br>          notes={this.props.notes}<br><strong>          selectedNoteId={this.props.selectedNoteId}<br>          onClickNote={this.props.onClickNote}</strong><br>        /&gt;<br>        &lt;NoteEditor /&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteContainer;</pre><p>Right now this component is only passing the props further into the &lt;NoteSelectors&gt; component. The code in <strong>src/components/NoteSelectors.js</strong> looks like:</p><pre>import React, { Component } from &#39;react&#39;;<br>import NoteSelector from &#39;./NoteSelector&#39;;<br><br>class NoteSelectors extends Component {<br>  render() {<br>    const sortedNotes = this.props.notes.slice().sort((a, b) =&gt; <br>      b.timestamp - a.timestamp<br>    )<br>    const noteSelectors = sortedNotes.map(note =&gt; <br>      &lt;NoteSelector<br>        key={note.id}<br>        body={note.body}<br>        timestamp={note.timestamp}<br>        <strong>id={note.id}</strong><br>        <strong>selectedNoteId={this.props.selectedNoteId}<br>        onClickNote={this.props.onClickNote}</strong><br>      /&gt;<br>    );<br><br>    return (<br>      &lt;div className=&quot;note-selectors&quot;&gt;<br>        {noteSelectors}<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteSelectors;</pre><p>Again, this component is only passing the props further into the &lt;NoteSelector&gt; component. Note that it’s also adding one more prop for the note id, which will be useful in a moment. Finally, we’ll see how the props get used in <strong>src/components/NoteSelector.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br><br>function formatTitle(body) {<br>  var maxLength = 20;<br>  if (body.length &gt; maxLength) {<br>    return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>  } else if (body.length === 0) {<br>    return &quot;New note&quot;;<br>  } else {<br>    return body;<br>  }<br>}<br><br>function formatTimestamp(timestamp) {<br>  return new Date(timestamp).toUTCString();<br>}<br><br>class NoteSelector extends Component {<br><strong>  handleClickNote = () =&gt; {<br>    this.props.onClickNote(this.props.id);<br>  }</strong><br><br>  render() {<br>    return (<br>      &lt;div<br>        <strong>className={&quot;note-selector &quot; + (this.props.id === this.props.selectedNoteId ? &#39;active&#39; : &#39;&#39;)}</strong><br>        <strong>onClick={this.handleClickNote}</strong><br>      &gt;<br>        &lt;p className=&quot;note-selector-title&quot;&gt;<br>          {formatTitle(this.props.body)}<br>        &lt;/p&gt;<br>        &lt;p className=&quot;note-selector-timestamp&quot;&gt;<br>          {formatTimestamp(this.props.timestamp)}<br>        &lt;/p&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteSelector;</pre><p>There are three essential things happening here:</p><ul><li>The code onClick={this.handleClickNote} is capturing the DOM’s click handler and binding it to the current component’s handleClickNote method.</li><li>The handleClickNote method is defined to simply call the function onClickNote that was passed in from the props. Recall that this was defined in the &lt;App&gt; component to update the state of the selectedNoteId. Once the state is updated, React will automatically re-render all the children components.</li><li>The code className={&quot;note-selector &quot; + (this.props.id === this.props.selectedNoteId ? &#39;active&#39; : &#39;&#39;)} is using a JavaScript ternary operator to conditionally add the .active class to the div if the note is the selected note. Again, there isn’t any special syntax like Vue.js’ v-bind to apply this class name, you’re just using built in JavaScript tools.</li></ul><p>This is React’s way of managing components (props down, events up), which helps keep the architecture of the code clean and avoids jQuery spaghetti that we saw a bit of <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">earlier</a>. It’s a bit different than Vue.js’ component model — in Vue.js, each component can only pass emit and listen to events from its direct child and direct parent. In React, you don’t emit events, instead you pass functions (which can belong to any component) in props, which means the function can be called by a child, grandchild, etc. In this case the &lt;NoteSelector&gt; component is essentially the great-great-grandchild of the &lt;App&gt; component, but it is able to call the onClickNote prop which is directly connected to the &lt;App&gt; component. In Vue.js you would have to bubble up the event one component at a time.</p><h3>Edit the selected note on editor input</h3><p>At this point we can select a note and it highlights accordingly, but the content of the selected note aren’t showing up in the &lt;NoteEditor&gt; component. We also need to be able to update the selected note as the user enters text in the editor. Here’s how the code would change in <strong>src/App.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br>import Toolbar from &#39;./components/Toolbar&#39;<br>import NoteContainer from &#39;./components/NoteContainer&#39;<br>import &#39;./App.css&#39;;<br><br>class App extends Component {<br>  constructor(props) {<br>    super(props);<br>    this.state = {<br>      notes: [<br>        {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>        {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>        {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>      ],<br>      selectedNoteId: 1<br>    }<br>  }<br><br>  handleClickNote = (id) =&gt; {<br>    this.setState({selectedNoteId: id});<br>  }<br><br><strong>  handleNoteEditorChange = (text) =&gt; {<br>    const newNotes = this.state.notes.map((note) =&gt; {<br>      if (note.id === this.state.selectedNoteId) {<br>        return {<br>          id: note.id,<br>          body: text,<br>          timestamp: Date.now()<br>        };<br>      } else {<br>        return note;<br>      }<br>    });<br>    this.setState({notes: newNotes});<br>  }<br><br></strong>  render() {<br>    return (<br>      &lt;div id=&quot;app&quot;&gt;<br>        &lt;Toolbar /&gt;<br>        &lt;NoteContainer<br>          notes={this.state.notes}<br>          selectedNoteId={this.state.selectedNoteId}<br>          onClickNote={this.handleClickNote}<br>          <strong>onNoteEditorChange={this.handleNoteEditorChange}</strong><br>        /&gt;<br>      &lt;/div&gt;      <br>    );<br>  }<br>}<br><br>export default App;</pre><p>The handleNoteEditorChange is a method that updates the selected note’s body with the given text. The thing that makes it tricky is that React requires you to not directly mutate the state object; instead, you should create a new copy of the part of the state that’s changing and update it using the setState function. Here we’re using the built-in array map function to create a new copy of the notes, where each note remains unchanged except for the current selected note, which gets recreated with the new body and timestamp.</p><p>In the render function, we’re passing the handleNoteEditorChange method the &lt;NoteContainer&gt; component as a prop named onNoteEditorChange. To use the method, the code in <strong>src/components/NoteContainer.js</strong> would change as follows:</p><pre>import React, { Component } from &#39;react&#39;;<br>import NoteSelectors from &#39;./NoteSelectors&#39;;<br>import NoteEditor from &#39;./NoteEditor&#39;;<br><br>class NoteContainer extends Component {<br>  render() {<br><strong>    const selectedNote = this.props.notes.find(note =&gt;<br>      note.id === this.props.selectedNoteId<br>    );<br><br></strong>    return (<br>      &lt;div className=&quot;note-container&quot;&gt;<br>        &lt;NoteSelectors<br>          notes={this.props.notes}<br>          selectedNoteId={this.props.selectedNoteId}<br>          onClickNote={this.props.onClickNote}<br>        /&gt;<br>        &lt;NoteEditor<br><strong>          selectedNote={selectedNote}<br>          onNoteEditorChange={this.props.onNoteEditorChange}</strong><br>        /&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteContainer;</pre><p>First we’re creating a variable called selectedNote, which we have to find from the given notes and selectedNoteId in props. Then we can bind the selectedNote to the &lt;NoteEditor&gt; props, along with the given onNoteEditorChange function. (Note that we have to find the selectedNote here as opposed to inside the &lt;NoteEditor&gt; component, because this component has access to both the notes and the selectedNoteId, whereas the &lt;NoteEditor&gt; doesn’t have access to the notes data).</p><p>Finally, we can see how the &lt;NoteEditor&gt; component will use the props in <strong>src/components/NoteEditor.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br><br><strong>function formatTimestamp(timestamp) {<br>  return new Date(timestamp).toUTCString();<br>}<br><br></strong>class NoteEditor extends Component {<br><strong>  handleChange = (event) =&gt; {<br>    this.props.onNoteEditorChange(event.target.value);<br>  }<br><br></strong>  render() {<br>    return (<br>      &lt;div className=&quot;note-editor&quot;&gt;<br>        &lt;p className=&quot;note-editor-info&quot;&gt;<br>          <strong>{formatTimestamp(this.props.selectedNote.timestamp)}</strong><br>        &lt;/p&gt;<br>        &lt;textarea className=&quot;note-editor-input&quot;<br>          <strong>value={this.props.selectedNote.body}</strong><br>          <strong>onChange={this.handleChange}</strong><br>        /&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteEditor;</pre><p>Let’s go over what’s going on here one at a time:</p><ul><li>We need to define a formatTimestamp function to format the editor’s timestamp. It’s the same as the function defined in the &lt;NoteSelector&gt; component, so we’ll need to find a way to DRY it up (in the next section).</li><li>The handleChange method is going to be triggered from a DOM input event, and it will simply call up the onNoteEditorChange method that was given in the props (first extracting out the user’s text from the event).</li><li>The render method displays the selectedNote’s timestamp and body, as well as bind’s the DOM’s &lt;textarea&gt; input event to run the handleChange method defined above.</li></ul><p>Again, the flow would be the user enters text in the &lt;NoteEditor&gt; component, which triggers its handleChange method, which runs the onNoteEditorChange method from its props, which all the way in the &lt;App&gt; component updates the notes in its state object, causing all the children to be re-rendered.</p><h3>Refactor helper functions to separate file</h3><p>Right now we have an issue where we defined the same formatTimestamp function in two different components. Again, React doesn’t provide a React-specific way to handle this issue — you can use regular JavaScript to DRY up the code. In this case we’re going to write the helpers in a separate file, then import them as needed (which is not traditional JavaScript per se, but we’ve been using imports repeatedly throughout the code, so it shouldn’t be that hard). Here’s the new helper file in <strong>src/helpers.js</strong>:</p><pre>export function formatTitle(body) {<br>  var maxLength = 20;<br>  if (body.length &gt; maxLength) {<br>    return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>  } else if (body.length === 0) {<br>    return &quot;New note&quot;;<br>  } else {<br>    return body;<br>  }<br>}<br><br>export function formatTimestamp(timestamp) {<br>  return new Date(timestamp).toUTCString();<br>}</pre><p>And here’s how you would import the functions in <strong>src/components/NoteSelector.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br><strong>import {formatTitle, formatTimestamp} from &#39;../helpers&#39;;<br><br></strong>class NoteSelector extends Component {<br>  handleClickNote = () =&gt; {<br>    this.props.onClickNote(this.props.id);<br>  }<br><br>  render() {<br>    return (<br>      &lt;div<br>        className={&quot;note-selector &quot; + (this.props.id === this.props.selectedNoteId ? &#39;active&#39; : &#39;&#39;)}<br>        onClick={this.handleClickNote}<br>      &gt;<br>        &lt;p className=&quot;note-selector-title&quot;&gt;<br>          {formatTitle(this.props.body)}<br>        &lt;/p&gt;<br>        &lt;p className=&quot;note-selector-timestamp&quot;&gt;<br>          {formatTimestamp(this.props.timestamp)}<br>        &lt;/p&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteSelector;</pre><p>You would do the same in <strong>src/components/NoteEditor.js</strong> to import the formatTimestamp function. This is an easy enough structure to put any helper functions in an organized spot going forward!</p><h3>Create a new note with a button</h3><p>Now let’s implement the ability to create a new note. Clicking on the “New” button should create a new note (new id, no body, current timestamp). The new note should become the currently selected note and appear at the top of the list of note selectors. Let’s start with the child &lt;Toolbar&gt; component in <strong>src/components/Toolbar.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br><br>class Toolbar extends Component {<br>  render() {<br>    return (<br>      &lt;div className=&quot;toolbar&quot;&gt;<br>        &lt;button className=&quot;toolbar-button&quot; <strong>onClick={this.props.onNewNote}</strong>&gt;New&lt;/button&gt;<br>        &lt;button className=&quot;toolbar-button&quot;&gt;Delete&lt;/button&gt;<br>        &lt;input className=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot;&gt;&lt;/input&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default Toolbar;</pre><p>Here the code onClick={this.props.onNewNote} is capturing the native DOM &lt;button&gt; click event with onClick, which will run the onNewNote method from the props when the event fires. This means the parent &lt;App&gt; component must send down the onNewNote method in props appropriately, which would change the code in <strong>src/App.js</strong> as follows:</p><pre>import React, { Component } from &#39;react&#39;;<br>import Toolbar from &#39;./components/Toolbar&#39;<br>import NoteContainer from &#39;./components/NoteContainer&#39;<br>import &#39;./App.css&#39;;<br><br>class App extends Component {<br>  constructor(props) {<br>    super(props);<br>    this.state = {<br>      notes: [<br>        {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>        {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>        {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>      ],<br>      selectedNoteId: 1<br>    }<br>  }<br><br>  handleClickNote = (id) =&gt; {<br>    this.setState({selectedNoteId: id});<br>  }<br><br>  handleNoteEditorChange = (text) =&gt; {<br>    const newNotes = this.state.notes.map((note) =&gt; {<br>      if (note.id === this.state.selectedNoteId) {<br>        return {<br>          id: note.id,<br>          body: text,<br>          timestamp: Date.now()<br>        };<br>      } else {<br>        return note;<br>      }<br>    });<br>    this.setState({notes: newNotes});<br>  }<br><br><strong>  handleNewNote = () =&gt; {<br>    const newNote = {<br>      id: Date.now(),<br>      body: &quot;&quot;,<br>      timestamp: Date.now()<br>    }<br>    this.setState({<br>      notes: this.state.notes.concat([newNote]),<br>      selectedNoteId: newNote.id<br>    })<br>  }</strong><br><br>  render() {<br>    return (<br>      &lt;div id=&quot;app&quot;&gt;<br>        &lt;Toolbar<br>          <strong>onNewNote={this.handleNewNote}</strong><br>        /&gt;<br>        &lt;NoteContainer<br>          notes={this.state.notes}<br>          selectedNoteId={this.state.selectedNoteId}<br>          onClickNote={this.handleClickNote}<br>          onNoteEditorChange={this.handleNoteEditorChange}<br>        /&gt;<br>      &lt;/div&gt;      <br>    );<br>  }<br>}<br><br>export default App;</pre><p>Here the &lt;Toolbar&gt; component is sending the handleNewNote method as a prop called onNewNote (which we defined earlier to run whenever the “New” button in the toolbar is clicked). The handleNewNote method has to update the state with a new note as well as a new selected note id, which again requires care to avoid direct mutation. Here we’re creating a new copy of the notes array by using the array concat method. Once the state is updated with setState, React will automatically re-render the child components as needed.</p><h3>Delete the selected note with a button</h3><p>Wiring up this feature starts off pretty similar to the new note feature. Let’s again start with the child &lt;Toolbar&gt; component in <strong>src/components/Toolbar.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br><br>class Toolbar extends Component {<br>  render() {<br>    return (<br>      &lt;div className=&quot;toolbar&quot;&gt;<br>        &lt;button className=&quot;toolbar-button&quot; onClick={this.props.onNewNote}&gt;New&lt;/button&gt;<br>        &lt;button className=&quot;toolbar-button&quot; <strong>onClick={this.props.onDeleteNote}</strong>&gt;Delete&lt;/button&gt;<br>        &lt;input className=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot;&gt;&lt;/input&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default Toolbar;</pre><p>Here the code onClick={this.props.onDeleteNote} is capturing the native DOM &lt;button&gt; click event and running the onDeleteNote method from the props. This means the parent &lt;App&gt; component must send down the props appropriately, which would change the code in <strong>src/App.js</strong> as follows:</p><pre>import React, { Component } from &#39;react&#39;;<br>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;<br><strong>import {transformNotes} from &#39;./helpers&#39;;</strong><br>import &#39;./App.css&#39;;<br><br>class App extends Component {<br>  constructor(props) {<br>    super(props);<br>    this.state = {<br>      notes: [<br>        {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>        {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>        {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>      ],<br>      selectedNoteId: 1<br>    }<br>  }<br><br>  handleClickNote = (id) =&gt; {<br>    this.setState({selectedNoteId: id});<br>  }<br><br>  handleNoteEditorChange = (text) =&gt; {<br>    const newNotes = this.state.notes.map((note) =&gt; {<br>      if (note.id === this.state.selectedNoteId) {<br>        return {<br>          id: note.id,<br>          body: text,<br>          timestamp: Date.now()<br>        };<br>      } else {<br>        return note;<br>      }<br>    });<br>    this.setState({notes: newNotes});<br>  }<br><br>  handleNewNote = () =&gt; {<br>    const newNote = {<br>      id: Date.now(),<br>      body: &quot;&quot;,<br>      timestamp: Date.now()<br>    }<br>    this.setState({<br>      notes: this.state.notes.concat([newNote]),<br>      selectedNoteId: newNote.id<br>    })<br>  }<br><br><strong>  handleDeleteNote = () =&gt; {<br>    const newNotes = this.state.notes.filter(note =&gt;<br>      note.id !== this.state.selectedNoteId<br>    );<br>    const transformedNotes = transformNotes(newNotes);<br>    const newSelectedNoteId = transformedNotes.length &gt; 0 ? transformedNotes[0].id : null<br>    this.setState({<br>      notes: newNotes,<br>      selectedNoteId: newSelectedNoteId<br>    });<br>  }</strong><br><br>  render() {<br>    return (<br>      &lt;div id=&quot;app&quot;&gt;<br>        &lt;Toolbar<br>          onNewNote={this.handleNewNote}<br>          <strong>onDeleteNote={this.handleDeleteNote}</strong><br>        /&gt;<br>        &lt;NoteContainer<br>          notes={this.state.notes}<br>          selectedNoteId={this.state.selectedNoteId}<br>          onClickNote={this.handleClickNote}<br>          onNoteEditorChange={this.handleNoteEditorChange}<br>        /&gt;<br>      &lt;/div&gt;      <br>    );<br>  }<br>}<br><br>export default App;</pre><p>Here the &lt;Toolbar&gt; component is sending the handleDeleteNote method as a prop called onDeleteNote, which we defined earlier to run whenever the “Delete” button in the toolbar is clicked. The handleDeleteNote method has to update the state with a deleted note as well as a new selected note id, which again requires care to avoid mutation. Here we’re creating a new copy of the notes array by using the array filter method. Once the state is updated with setState, React will automatically re-render the child components as needed.</p><p>However, there’s a problem — the handleDeleteNote method also needs to select a new note in place of the one that’s deleted. The problem is that selecting a new note means we need to know the top of the list of the <strong>transformed </strong>(sorted) notes, which we defined in the child &lt;NoteSelectors&gt;component. That seemed like the right decision at the time, but now we’ll need to refactor that method to be in the <strong>src/helpers.js</strong> file as follows:</p><pre>export function formatTitle(body) {<br>  var maxLength = 20;<br>  if (body.length &gt; maxLength) {<br>    return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>  } else if (body.length === 0) {<br>    return &quot;New note&quot;;<br>  } else {<br>    return body;<br>  }<br>}<br><br>export function formatTimestamp(timestamp) {<br>  return new Date(timestamp).toUTCString();<br>}<br><br><strong>export function transformNotes(notes) {<br>  return notes.slice().sort((a, b) =&gt;<br>    b.timestamp - a.timestamp<br>  );<br>}</strong></pre><p>The &lt;NoteSelectors&gt; component in <strong>src/components/NoteSelectors.js</strong> can now import the transformNotes helper function as well:</p><pre>import React, { Component } from &#39;react&#39;;<br>import NoteSelector from &#39;./NoteSelector&#39;;<br><strong>import {transformNotes} from &#39;../helpers&#39;;<br><br></strong>class NoteSelectors extends Component {<br>  render() {<br>    const noteSelectors = <strong>transformNotes</strong>(this.props.notes).map(note =&gt;<br>      &lt;NoteSelector<br>        key={note.id}<br>        body={note.body}<br>        timestamp={note.timestamp}<br>        id={note.id}<br>        selectedNoteId={this.props.selectedNoteId}<br>        onClickNote={this.props.onClickNote}<br>      /&gt;<br>    );<br><br>    return (<br>      &lt;div className=&quot;note-selectors&quot;&gt;<br>        {noteSelectors}<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteSelectors;</pre><p>The last detail is to change the &lt;NoteEditor&gt; component to only render if there is a selectedNote, since it’s now possible to delete all the notes. The template in <strong>src/components/NoteEditor.js</strong> would change as follows:</p><pre>import React, { Component } from &#39;react&#39;;<br>import {formatTimestamp} from &#39;../helpers&#39;;<br><br>class NoteEditor extends Component {<br>  handleChange = (event) =&gt; {<br>    this.props.onNoteEditorChange(event.target.value);<br>  }<br><br>  render() {<br>    <strong>if (this.props.selectedNote) {</strong><br>      return (<br>        &lt;div className=&quot;note-editor&quot;&gt;<br>          &lt;p className=&quot;note-editor-info&quot;&gt;<br>            {formatTimestamp(this.props.selectedNote.timestamp)}<br>          &lt;/p&gt;<br>          &lt;textarea className=&quot;note-editor-input&quot;<br>            value={this.props.selectedNote.body}<br>            onChange={this.handleChange}<br>          /&gt;<br>        &lt;/div&gt;<br>      );<br><strong>    } else {<br>      return null;<br>    }</strong><br>  }<br>}<br><br>export default NoteEditor;</pre><p>Again, no special React syntax, just a regular JavaScript if condition on the render method (returning null to skip rendering the component).</p><h3>Filter notes on search input</h3><p>The final feature is to be able to search notes immediately as you type in the search input. Let’s again start with the child &lt;Toolbar&gt; component in <strong>src/components/Toolbar.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br><br>class Toolbar extends Component {<br><strong>  handleInput = (event) =&gt; {<br>    this.props.onSearchNote(event.target.value);<br>  }<br><br></strong>  render() {<br>    return (<br>      &lt;div className=&quot;toolbar&quot;&gt;<br>        &lt;button className=&quot;toolbar-button&quot; onClick={this.props.onNewNote}&gt;New&lt;/button&gt;<br>        &lt;button className=&quot;toolbar-button&quot; onClick={this.props.onDeleteNote}&gt;Delete&lt;/button&gt;<br>        &lt;input className=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot; <strong>onInput={this.handleInput}</strong>&gt;&lt;/input&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default Toolbar;</pre><p>Here the code onInput={this.handleInput} is capturing the native DOM &lt;input&gt; input event and running the handleInput method. The handleInput method simply calls up the onSearchNote method from the props, giving it the value of the user’s text. This means the parent &lt;App&gt; component must send down the props appropriately, which would change the code in <strong>src/App.js</strong> as follows:</p><pre>import React, { Component } from &#39;react&#39;;<br>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;<br>import {transformNotes} from &#39;./helpers&#39;;<br>import &#39;./App.css&#39;;<br><br>class App extends Component {<br>  constructor(props) {<br>    super(props);<br>    this.state = {<br>      notes: [<br>        {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>        {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>        {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>      ],<br>      selectedNoteId: 1,<br>      <strong>searchText: &quot;&quot;</strong><br>    }<br>  }<br><br>  handleClickNote = (id) =&gt; {<br>    this.setState({selectedNoteId: id});<br>  }<br><br>  handleNoteEditorChange = (text) =&gt; {<br>    const newNotes = this.state.notes.map((note) =&gt; {<br>      if (note.id === this.state.selectedNoteId) {<br>        return {<br>          id: note.id,<br>          body: text,<br>          timestamp: Date.now()<br>        };<br>      } else {<br>        return note;<br>      }<br>    });<br>    this.setState({notes: newNotes});<br>  }<br><br>  handleNewNote = () =&gt; {<br>    const newNote = {<br>      id: Date.now(),<br>      body: &quot;&quot;,<br>      timestamp: Date.now()<br>    }<br>    this.setState({<br>      notes: this.state.notes.concat([newNote]),<br>      selectedNoteId: newNote.id<br>    })<br>  }<br><br>  handleDeleteNote = () =&gt; {<br>    const newNotes = this.state.notes.filter(note =&gt;<br>      note.id !== this.state.selectedNoteId<br>    );<br>    const transformedNotes = <strong>transformNotes(newNotes, this.state.searchText)</strong>;<br>    const newSelectedNoteId = transformedNotes.length &gt; 0 ? transformedNotes[0].id : null<br>    this.setState({<br>      notes: newNotes,<br>      selectedNoteId: newSelectedNoteId<br>    });<br>  }<br><br><strong>  handleSearchNote = (newSearchText) =&gt; {<br>    const transformedNotes = transformNotes(this.state.notes, newSearchText);<br>    let newSelectedNoteId = null;<br>    if (transformedNotes.length &gt; 0) {<br>      const selectedNote = transformedNotes.find(note =&gt; note.id === this.state.selectedNoteId);<br>      if (selectedNote) {<br>        newSelectedNoteId = selectedNote.id;<br>      } else {<br>        newSelectedNoteId = transformedNotes[0].id;<br>      }<br>    }<br>    this.setState({<br>      searchText: newSearchText,<br>      selectedNoteId: newSelectedNoteId<br>    })<br>  }<br><br></strong>  render() {<br>    return (<br>      &lt;div id=&quot;app&quot;&gt;<br>        &lt;Toolbar<br>          onNewNote={this.handleNewNote}<br>          onDeleteNote={this.handleDeleteNote}<br>          <strong>onSearchNote={this.handleSearchNote}</strong><br>        /&gt;<br>        &lt;NoteContainer<br>          notes={this.state.notes}<br>          selectedNoteId={this.state.selectedNoteId}<br>          <strong>searchText={this.state.searchText}</strong><br>          onClickNote={this.handleClickNote}<br>          onNoteEditorChange={this.handleNoteEditorChange}<br>        /&gt;<br>      &lt;/div&gt;      <br>    );<br>  }<br>}<br><br>export default App;</pre><p>Let’s go through these changes one at a time:</p><ul><li>We are keeping track of a new piece of state, searchText — the search text the user enters. This will be used to filter the list of notes.</li><li>The transformNotes helper method now takes in two arguments — the array of notes as well as the searchText. The transformation will now consist of both filtering and sorting (see below).</li><li>The handleSearchNote method updates the searchText data to the input argument, which automatically triggers React to re-render all the child components. It also has to update the selectedNoteId to be the first of the transformedNotes if possible.</li><li>The &lt;Toolbar&gt; component now takes in a new prop onSearchNote={this.handleSearchNote} so it can call up the handleSearchNote function whenever the search input text changes.</li><li>The &lt;NoteContainer&gt; component now takes in a new prop searchText={this.state.searchText}<strong>, </strong>which the children components will need to properly filter the notes as well.</li></ul><p>The transformNotes helper function in <strong>src/helpers.js</strong> now looks like:</p><pre>export function formatTitle(body) {<br>  var maxLength = 20;<br>  if (body.length &gt; maxLength) {<br>    return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>  } else if (body.length === 0) {<br>    return &quot;New note&quot;;<br>  } else {<br>    return body;<br>  }<br>}<br><br>export function formatTimestamp(timestamp) {<br>  return new Date(timestamp).toUTCString();<br>}<br><br><strong>export function transformNotes(notes, searchText) {<br>  return notes<br>    .filter(note =&gt;<br>      note.body.toLowerCase().indexOf(searchText.toLowerCase()) !== -1<br>    )<br>    .sort((a, b) =&gt;<br>      b.timestamp - a.timestamp<br>    );<br>}</strong></pre><p>The &lt;NoteContainer&gt; component changes slightly to continue to pass along the searchText prop, which you can see in <strong>src/components/NoteContainer.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br>import NoteSelectors from &#39;./NoteSelectors&#39;;<br>import NoteEditor from &#39;./NoteEditor&#39;;<br><br>class NoteContainer extends Component {<br>  render() {<br>    const selectedNote = this.props.notes.find(note =&gt; note.id === this.props.selectedNoteId);<br><br>    return (<br>      &lt;div className=&quot;note-container&quot;&gt;<br>        &lt;NoteSelectors<br>          notes={this.props.notes}<br>          selectedNoteId={this.props.selectedNoteId}<br>          <strong>searchText={this.props.searchText}</strong><br>          onClickNote={this.props.onClickNote}<br>        /&gt;<br>        &lt;NoteEditor<br>          selectedNote={selectedNote}<br>          onNoteEditorChange={this.props.onNoteEditorChange}<br>        /&gt;<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteContainer;</pre><p>Finally, the &lt;NoteSelectors&gt; component uses the searchText prop to filter the notes using the transformNotes helper function, which you can see in <strong>src/components/NoteSelectors.js</strong>:</p><pre>import React, { Component } from &#39;react&#39;;<br>import NoteSelector from &#39;./NoteSelector&#39;;<br>import {transformNotes} from &#39;../helpers&#39;;<br><br>class NoteSelectors extends Component {<br>  render() {<br><strong>    const transformedNotes = transformNotes(this.props.notes, this.props.searchText);<br>    const noteSelectors = transformedNotes.map(note =&gt;</strong><br>      &lt;NoteSelector<br>        key={note.id}<br>        body={note.body}<br>        timestamp={note.timestamp}<br>        id={note.id}<br>        selectedNoteId={this.props.selectedNoteId}<br>        onClickNote={this.props.onClickNote}<br>      /&gt;<br>    );<br><br>    return (<br>      &lt;div className=&quot;note-selectors&quot;&gt;<br>        {noteSelectors}<br>      &lt;/div&gt;<br>    );<br>  }<br>}<br><br>export default NoteSelectors;</pre><p>You can see the final version of the code in all its glory at this <a href="https://github.com/peterxjang/notes-app-react/tree/94f2d8a105c0e224490325a4bac1b1d2266961a1">GitHub repository</a>.</p><h3>Conclusion</h3><p>In many ways, working with React components is quite similar to working with <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Vue.js components</a> (Vue.js was definitely heavily inspired by both Angular 1 and React). There’s definitely something nice about React providing a small API surface, without a lot of specialized approaches — it forces you to get better at your raw JavaScript skills. You need a loop to generate HTML from a list? Use the JavaScript array map method. You need conditional rendering? Use a regular JavaScript if condition. You want helper functions? Make functions in a file and import them. Pretty cool stuff.</p><p>However, at the same time React leans so heavily on new JavaScript features that it can definitely feel daunting to learn up front. And despite not having to learn React-specific syntax, you do need to accustom yourself to quite a bit of React specific idioms (naming event handlers, properly binding this without triggering extra re-rendering, avoiding mutation, etc.). Also, React can sometimes be unopinionated to the point where it creates confusion — there’s many different ways to define a component (the class keyword, pure functions, etc.), and quite a bit of debate on what’s the “best” way to do it.</p><p>React at the time of this writing is a pretty interesting blend of (what at least looks like) object oriented approaches and functional approaches. It’s definitely interesting to contrast with Vue.js, which encourages mutation of data as a key way of providing reactivity. React prefers an immutable approach to state, which can be at times tricky to achieve with JavaScript as a language. In the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-6-elm-578714526164">next part</a> of this series, we’ll be looking at <a href="http://elm-lang.org/">Elm</a>, which is an actual pure functional language with immutable data structures that compiles to JavaScript. Stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b51fd7d075fe" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/comparing-frontend-frameworks-part-5-react-b51fd7d075fe">Comparing Frontend Approaches Part 5: React</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Comparing Frontend Approaches Part 4: Vue.js with Components]]></title>
            <link>https://medium.com/actualize-network/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/675c880d4585</guid>
            <category><![CDATA[vuejs]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Sat, 13 Jan 2018 23:14:08 GMT</pubDate>
            <atom:updated>2019-04-14T13:28:52.011Z</atom:updated>
            <content:encoded><![CDATA[<h4>A look at jQuery, Vue.js, React, and Elm</h4><ul><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">Part 1: Introduction</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">Part 2: jQuery</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">Part 3: Vue.js</a></li><li><strong>Part 4: Vue.js with components</strong></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-5-react-b51fd7d075fe">Part 5: React</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-6-elm-578714526164">Part 6: Elm</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-approaches-part-7-final-thoughts-69cdba516f86">Part 7: Final thoughts</a></li></ul><p>In this part we will be implementing the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">web based clone</a> of the <a href="https://support.apple.com/kb/PH22608?locale=en_US">Mac Notes app</a> using <a href="https://vuejs.org/">Vue.js</a>, but this time with components. As mentioned last time, Vue.js labels itself as “the progressive JavaScript framework”, which means you learn and implement the advanced functionality only as needed. One such feature is using components, which are a great way to organize code once an app becomes sufficiently complex.</p><p>Using components to isolate behavior and increase reusability isn’t a new programming concept, but it is new for frontend programming. Native <a href="https://en.wikipedia.org/wiki/Web_Components">web components</a> for the browser were first announced in 2011 but never fully landed (as of 2017). JavaScript frameworks picked up the slack, providing an immediate way to build complex apps with a component architecture. However, there are significant tradeoffs to using components — setup, build tools, passing data all becomes harder compared to prior approaches. Let’s see what it looks like in practice to evaluate the tradeoffs ourselves!</p><blockquote>Note — the focus of this part is largely on the Vue.js component model itself. If you want a broader look at Vue.js features such as basic directives and options, I would recommend starting with the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">previous part</a> of this series where I build the same app using Vue.js without components.</blockquote><h3>Installation</h3><p>The installation process for Vue.js with components is pretty different than installing <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">jQuery</a> and <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">Vue.js without components</a>. This is because using components naturally lends itself to writing code in separate files, which means we’ll need a build process to bundle our JavaScript. This would normally be a lot of work, but fortunately Vue.js offers vue-cli, a command line tool to scaffold out projects. If you don’t already have it installed, you can install vue-cli with the command:</p><pre>$ npm install -g @vue/cli</pre><p>Once you have it installed, you can navigate to the directory you want to create your project in and run:</p><pre>$ vue create my-project</pre><p>You should replace my-project with whatever name you want your particular project to have. This will prompt you with setup options (you can choose the default setup). In the end you will end up a new folder called my-project with the following project structure:</p><pre>.<br>├── node_modules/<br>│   └── ...<br>├── public/<br>│   ├── favicon.ico<br>│   └── index.html<br>├── src/<br>│   ├── assets/<br>│   │   └── logo.png<br>│   ├── components/<br>│   │   └── HelloWorld.vue<br>│   ├── App.vue<br>│   └── main.js<br>├── .gitignore<br>├── babel.config.js<br>├── package-lock.json<br>├── package.json<br>└── README.md</pre><p>Whew, that’s a lot of files! Fortunately you don’t need to worry about most of them for right now (you can read more about the project structure from the <a href="http://vuejs-templates.github.io/webpack/structure.html">official guide</a>), pretty much all of our work will be in the <strong>src</strong> directory. At this point you already have a hello world app built for you, which you can see by entering the following commands:</p><pre>$ cd my-project<br>$ npm run serve</pre><p>The last command will run a dev server and automatically open up your browser to localhost:8080, where you should see something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*L90vI1YkeijHlo--WEmdsw.png" /></figure><p>This is pretty cool, and what’s cooler is that you have a hot-reloading environment already set up for you. What that means is that if you make changes to your code, the browser at localhost:8080 will automatically refresh. The place to start making changes is <strong>src/App.vue</strong>, which right now looks like this:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br>    &lt;img alt=&quot;Vue logo&quot; src=&quot;./assets/logo.png&quot;&gt;<br>    &lt;HelloWorld msg=&quot;Welcome to Your Vue.js App&quot;/&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;</pre><pre>&lt;script&gt;<br>import HelloWorld from &#39;./components/HelloWorld.vue&#39;</pre><pre>export default {<br>  name: &#39;app&#39;,<br>  components: {<br>    HelloWorld<br>  }<br>}<br>&lt;/script&gt;</pre><pre>&lt;style&gt;<br>#app {<br>  font-family: &#39;Avenir&#39;, Helvetica, Arial, sans-serif;<br>  -webkit-font-smoothing: antialiased;<br>  -moz-osx-font-smoothing: grayscale;<br>  text-align: center;<br>  color: #2c3e50;<br>  margin-top: 60px;<br>}<br>&lt;/style&gt;</pre><p>This is a Vue.js component — a single <strong>.vue</strong> file which contains the HTML template in a &lt;template&gt; tag, the JavaScript in a &lt;script&gt; tag, and the CSS in a &lt;style&gt; tag. Which means we can take the HTML and CSS from our <a href="https://jsfiddle.net/peterxjang/vtsjc5w9/">notes app template</a> and use it instead, which would make the <strong>src/App.vue</strong> look like this:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br>    &lt;div class=&quot;toolbar&quot;&gt;<br>      &lt;button class=&quot;toolbar-button&quot;&gt;New&lt;/button&gt;<br>      &lt;button class=&quot;toolbar-button&quot;&gt;Delete&lt;/button&gt;<br>      &lt;input class=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot;&gt;<br>    &lt;/div&gt;<br>    &lt;div class=&quot;note-container&quot;&gt;<br>      &lt;div class=&quot;note-selectors&quot;&gt;<br>        &lt;div class=&quot;note-selector active&quot;&gt;<br>          &lt;p class=&quot;note-selector-title&quot;&gt;First note...&lt;/p&gt;<br>          &lt;p class=&quot;note-selector-timestamp&quot;&gt;Timestamp here...&lt;/p&gt;<br>        &lt;/div&gt;<br>        &lt;div class=&quot;note-selector&quot;&gt;<br>          &lt;p class=&quot;note-selector-title&quot;&gt;Second note...&lt;/p&gt;<br>          &lt;p class=&quot;note-selector-timestamp&quot;&gt;Timestamp here...&lt;/p&gt;<br>        &lt;/div&gt;<br>        &lt;div class=&quot;note-selector&quot;&gt;<br>          &lt;p class=&quot;note-selector-title&quot;&gt;Third note...&lt;/p&gt;<br>          &lt;p class=&quot;note-selector-timestamp&quot;&gt;Timestamp here...&lt;/p&gt;<br>        &lt;/div&gt;<br>      &lt;/div&gt;<br>      &lt;div class=&quot;note-editor&quot;&gt;<br>        &lt;p class=&quot;note-editor-info&quot;&gt;Timestamp here...&lt;/p&gt;<br>        &lt;textarea class=&quot;note-editor-input&quot;&gt;<br>          First note...<br>          <br>          Note text here...<br>        &lt;/textarea&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>export default {<br>  name: &#39;app&#39;<br>};<br>&lt;/script&gt;<br><br>&lt;style&gt;<br>/* RESET */<br>* {<br>  margin: 0;<br>  padding: 0;<br>  border: 0;<br>  outline: none;<br>  box-sizing: border-box;<br>}<br><br>/* LAYOUT */<br>#app {<br>  display: flex;<br>  flex-direction: column;<br>  min-height: 100vh;<br>}<br>.toolbar {<br>  padding: 0.5em;<br>}<br>.toolbar-button, .toolbar-search {<br>  padding: inherit;<br>  border-radius: 0.3em;<br>}<br>.toolbar-search {<br>  float: right;<br>}<br>.note-container {<br>  display: flex;<br>  flex: 1;<br>}<br>.note-selectors {<br>  flex: 0 0 13em;<br>}<br>.note-selector {<br>  padding: 1em;<br>}<br>.note-selector p {<br>  margin: 0;<br>}<br>.note-editor {<br>  display: flex;<br>  flex: 1;<br>  flex-direction: column;<br>}<br>.note-editor-info {<br>  padding: 0.5em;<br>  text-align: center;<br>}<br>.note-editor-input {<br>  display: flex;<br>  flex: 1;<br>  width: 100%;<br>  padding: 0 2em 0 2em;<br>}<br><br>/* COLORS */<br>* {<br>  color: #454545;<br>  background-color: #FAFAF8;<br>}<br>.toolbar {<br>  background-color: #DCDADC;<br>}<br>.toolbar-button {<br>  background-color: #FFFFFF;<br>}<br>.toolbar-button:active {<br>  background-color: #AAAAAA;<br>}<br>.note-selectors {<br>  border-right: 1px solid #DCDADC;<br>}<br>.note-selector {<br>  border-bottom: 1px solid #DCDADC;<br>}<br>.note-selector.active {<br>  background-color: #FCE18D;<br>}<br>.note-selector-title {<br>  background-color: inherit;<br>}<br>.note-selector-timestamp {<br>  color: #626262;<br>  background-color: inherit;<br>}<br>.note-editor-info {<br>  color: #DCDADC;<br>}<br><br>/* TYPOGRAPHY */<br>body {<br>  font-family: sans-serif;<br>}<br>.note-selector-title {<br>  font-weight: bold;<br>}<br>.note-selector-timestamp {<br>  font-size: 0.7em;<br>}<br>.note-editor, .note-editor-input {<br>  font-size: 0.9em;<br>}<br>&lt;/style&gt;</pre><p>If you look at your browser, you should see our familiar notes app starting point!</p><h3>Refactor into components</h3><p>The next step is to break this <strong>src/App.vue</strong> file into components. Here is a diagram of the different components we can break this app into:</p><pre>&lt;app&gt;<br>├── &lt;toolbar&gt;<br>└── &lt;note-container&gt;<br>    ├── &lt;note-selectors&gt;<br>    │   └── &lt;note-selector&gt;<br>    │   └── &lt;note-selector&gt;<br>    │   └── &lt;note-selector&gt;<br>    └── &lt;note-editor&gt;</pre><p>You could of course break it down even further (with individual button components for the toolbar for example), but this is a good starting point. Using this structure, the <strong>src/App.vue</strong> file would now look like:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br><strong>    &lt;toolbar&gt;&lt;/toolbar&gt;<br>    &lt;note-container&gt;&lt;/note-container&gt;</strong><br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br><strong>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;</strong><br><br>export default {<br>  name: &#39;app&#39;,<br><strong>  components: {<br>    Toolbar,<br>    NoteContainer<br>  }</strong><br>};<br>&lt;/script&gt;<br><br>&lt;style&gt;<br>/* RESET */<br>* {<br>  margin: 0;<br>  padding: 0;<br>  border: 0;<br>  outline: none;<br>  box-sizing: border-box;<br>}<br><br>/* LAYOUT */<br>#app {<br>  display: flex;<br>  flex-direction: column;<br>  min-height: 100vh;<br>}<br><br>/* COLORS */<br>* {<br>  color: #454545;<br>  background-color: #FAFAF8;<br>}<br><br>/* TYPOGRAPHY */<br>body {<br>  font-family: sans-serif;<br>}<br>&lt;/style&gt;</pre><p>The template essentially only contains the &lt;toolbar&gt; and &lt;note-container&gt; custom component tags, which are defined in the JavaScript. In the script tag, we import the components and register them with the Vue.js app. Finally, the style tag has changed only to include the CSS at the top level — any component specific styles will be defined within the components themselves. Let’s take a look at one of the sub-components, <strong>src/components/Toolbar.vue</strong>:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;toolbar&quot;&gt;<br>    &lt;button class=&quot;toolbar-button&quot;&gt;New&lt;/button&gt;<br>    &lt;button class=&quot;toolbar-button&quot;&gt;Delete&lt;/button&gt;<br>    &lt;input class=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot;&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>export default {<br>  name: &#39;toolbar&#39;<br>};<br>&lt;/script&gt;<br><br>&lt;style&gt;<br>/* LAYOUT */<br>.toolbar {<br>  padding: 0.5em;<br>}<br>.toolbar-button, .toolbar-search {<br>  padding: inherit;<br>  border-radius: 0.3em;<br>}<br>.toolbar-search {<br>  float: right;<br>}<br><br>/* COLORS */<br>.toolbar {<br>  background-color: #DCDADC;<br>}<br>.toolbar-button {<br>  background-color: #FFFFFF;<br>}<br>.toolbar-button:active {<br>  background-color: #AAAAAA;<br>}<br>&lt;/style&gt;</pre><p>As you can see, it’s the same as before — the template contains the HTML for just the toolbar, the script contains the JavaScript (which isn’t doing much right now), and the style contains the CSS for just the toolbar.</p><p>So breaking things into components is a pretty straightforward task. The harder part comes when dealing with data and events. If you want to see what the rest of the components look like right now (they pretty much follow the same pattern), you can check them out in full detail <a href="https://github.com/peterxjang/notes-app-vue-components/tree/1922751085f41c3af1501f419f96904527e93183/src/components">here</a>.</p><ul><li><a href="https://github.com/peterxjang/notes-app-vue-components/blob/1922751085f41c3af1501f419f96904527e93183/src/components/Toolbar.vue">Toolbar.vue</a></li><li><a href="https://github.com/peterxjang/notes-app-vue-components/blob/1922751085f41c3af1501f419f96904527e93183/src/components/NoteContainer.vue">NoteContainer.vue</a></li><li><a href="https://github.com/peterxjang/notes-app-vue-components/blob/1922751085f41c3af1501f419f96904527e93183/src/components/NoteSelectors.vue">NoteSelectors.vue</a></li><li><a href="https://github.com/peterxjang/notes-app-vue-components/blob/1922751085f41c3af1501f419f96904527e93183/src/components/NoteSelector.vue">NoteSelector.vue</a></li><li><a href="https://github.com/peterxjang/notes-app-vue-components/blob/1922751085f41c3af1501f419f96904527e93183/src/components/NoteEditor.vue">NoteEditor.vue</a></li></ul><h3>Display note titles from an array of notes</h3><p>Now that things are broken into components, we have to make some choices when it comes to storing and passing data. For the array of notes, it makes sense for the main &lt;App&gt; component to keep track of the notes and pass it down to its children. This would make <strong>src/App.vue</strong> template and script look like:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br>    &lt;toolbar&gt;&lt;/toolbar&gt;<br>    &lt;note-container <strong>v-bind:notes=&quot;notes&quot;</strong>&gt;&lt;/note-container&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;<br><br>export default {<br>  name: &#39;app&#39;,<br>  data: function() {<br>    return {<br><strong>      notes: [<br>        {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>        {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>        {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>      ]</strong><br>    };<br>  },<br>  components: {<br>    Toolbar,<br>    NoteContainer<br>  }<br>};<br>&lt;/script&gt;</pre><p>(Note — I’m not showing any &lt;style&gt; tags anymore because they won’t change from here on out). In the &lt;script&gt; tag, we define the notes array in the data option as we did in the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">previous Vue.js app</a>. In the &lt;template&gt; tag, we pass the notes data to the &lt;note-container&gt; component with the line:</p><pre>    &lt;note-container <strong>v-bind:notes=&quot;notes&quot;</strong>&gt;&lt;/note-container&gt;</pre><p>Here we’re binding a custom notes attribute to the component, which contains the array of notes defined in the data option. However, the &lt;note-container&gt; component doesn’t directly display the notes, so it has to pass it down using a similar technique. Here’s what the <strong>src/components/NoteContainer.vue</strong> template and script look like:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-container&quot;&gt;<br>    &lt;note-selectors <strong>v-bind:notes=&quot;notes&quot;</strong>&gt;&lt;/note-selectors&gt;<br>    &lt;note-editor&gt;&lt;/note-editor&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import NoteSelectors from &#39;./NoteSelectors&#39;;<br>import NoteEditor from &#39;./NoteEditor&#39;;<br><br>export default {<br>  name: &#39;note-container&#39;,<br>  <strong>props: [&#39;notes&#39;],</strong><br>  components: {<br>    NoteSelectors,<br>    NoteEditor<br>  }<br>};<br>&lt;/script&gt;</pre><p>In the &lt;script&gt; tag, we’re defining the notes as an element in the array of props. This is an important distinction — the &lt;note-container&gt; component doesn’t have notes defined in the data option, because it doesn’t define its own notes; rather, it’s getting the notes data from its parent. Data received from a parent is stored in the props option.</p><p>Again, the &lt;note-container&gt; component doesn’t deal with the notes directly, so it has to pass it down to the &lt;note-selectors&gt; component, which you see in the template line:</p><pre>    &lt;note-selectors <strong>v-bind:notes=&quot;notes&quot;</strong>&gt;&lt;/note-selectors&gt;</pre><p>Finally, we get to &lt;note-selectors&gt; component, which does actually use the notes. This is what the <strong>src/components/NoteSelectors.vue</strong> template and script look like:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-selectors&quot;&gt;<br><strong>    &lt;note-selector<br>      v-for=&quot;note in notes&quot;<br>      v-bind:note=&quot;note&quot;<br>      v-bind:key=&quot;note.id&quot;<br>    &gt;<br>    &lt;/note-selector&gt;</strong><br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import NoteSelector from &#39;./NoteSelector&#39;;<br><br>export default {<br>  name: &#39;note-selectors&#39;,<br>  <strong>props: [&#39;notes&#39;],</strong><br>  components: {<br>    NoteSelector<br>  }<br>};<br>&lt;/script&gt;</pre><p>Now we can use the notes and loop over them with a v-for directive to create as many &lt;note-selector&gt; components as we need. We need to bind each note object to the component, as well as a unique key to help Vue.js keep track of each component. This is what the <strong>src/components/NoteSelector.vue</strong> template and script look like:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-selector&quot;&gt;<br>    &lt;p class=&quot;note-selector-title&quot;&gt;<strong>{{ note.body }}</strong>&lt;/p&gt;<br>    &lt;p class=&quot;note-selector-timestamp&quot;&gt;<strong>{{ note.timestamp }}</strong>&lt;/p&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>export default {<br>  name: &#39;note-selector&#39;,<br>  <strong>props: [&#39;note&#39;]</strong><br>};<br>&lt;/script&gt;</pre><p>In the &lt;script&gt; tag we register the note as a prop (the key is used internally by Vue.js), and the &lt;template&gt; tag looks like a straightforward Vue.js template. For now, I’m using the note’s body and raw timestamp as placeholders, we’ll look at formatting them in the next section.</p><p>Whew, that was a lot of work just to loop through an array of notes! There definitely is a price to pay for breaking things into components — maintaining strong boundaries means you must put in more effort to pass information across said boundaries. However, although the concept of passing down data as props from component to component seems foreign at first, it gets to be second nature, since the patterns are fairly straightforward and repeatable.</p><h3>Use computed properties and filters to sort and format notes</h3><p>Right now the &lt;note-selector&gt; component needs formatting for the title and timestamp, which we can accomplish with Vue.js filters. This is what the <strong>src/components/NoteSelector.vue</strong> template and script look like with filters:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-selector&quot;&gt;<br>    &lt;p class=&quot;note-selector-title&quot;&gt;<br>      <strong>{{ note.body | formatTitle }}</strong><br>    &lt;/p&gt;<br>    &lt;p class=&quot;note-selector-timestamp&quot;&gt;<br>      <strong>{{ note.timestamp | formatTimestamp }}</strong><br>    &lt;/p&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;</pre><pre>&lt;script&gt;<br>export default {<br>  name: &#39;note-selector&#39;,<br><strong>  filters: {<br>    formatTitle: function(body) {<br>      var maxLength = 20;<br>      if (body.length &gt; maxLength) {<br>        return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>      } else if (body.length === 0) {<br>        return &quot;New note&quot;;<br>      } else {<br>        return body;<br>      }<br>    },<br>    formatTimestamp: function(timestamp) {<br>      return new Date(timestamp).toUTCString();<br>    }<br>  },</strong><br>  props: [&#39;note&#39;]<br>};<br>&lt;/script&gt;</pre><p>Also, the parent &lt;note-selectors&gt; component needs to sort the child&lt;note-selector&gt; components by timestamps. This is what the <strong>src/components/NoteSelectors.vue</strong> template and script look like:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-selectors&quot;&gt;<br>    &lt;note-selector<br>      <strong>v-for=&quot;note in transformedNotes&quot;</strong><br>      v-bind:note=&quot;note&quot;<br>      v-bind:key=&quot;note.id&quot;<br>    &gt;<br>    &lt;/note-selector&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import NoteSelector from &#39;./NoteSelector&#39;;<br><br>export default {<br>  name: &#39;note-selectors&#39;,<br>  props: [&#39;notes&#39;],<br><strong>  computed: {<br>    transformedNotes: function() {<br>      return this.notes.slice().sort(function(a, b) {<br>        return b.timestamp - a.timestamp;<br>      });<br>    }<br>  },</strong><br>  components: {<br>    NoteSelector<br>  }<br>};<br>&lt;/script&gt;</pre><p>This is pretty straightforward, and it begins to showcase the nice advantage of working with components — you can determine which component is responsible for which behavior and organize your code accordingly. Right now we have the &lt;note-selector&gt; component responsible for title and timestamp formatting, and the &lt;note-selectors&gt; parent component responsible for sorting. If there’s ever an issue, it makes it easier to locate and work on the appropriate code.</p><h3>Select a note on title click</h3><p>Now let’s implement the ability to actually select notes. Clicking on a note title should both highlight the selected note on the left as well as display the contents in the editor on the right. First we’ll change the template in <strong>src/App.vue</strong> as follows:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br>    &lt;toolbar&gt;&lt;/toolbar&gt;<br>    &lt;note-container<br>      v-bind:notes=&quot;notes&quot;<br><strong>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:selectNote=&quot;selectNote&quot;</strong><br>    &gt;<br>    &lt;/note-container&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;</pre><p>Here we’re sending a new prop and event to the &lt;note-container&gt; component. The names can be confusing, so it’s worth going over in detail.</p><ul><li>The code v-bind:selectedNote is essentially creating a new prop called selectedNote which will be available in &lt;note-container&gt;. It is being set equal to &quot;selectedNote&quot;, which is referring to a variable that must be defined in the current &lt;App&gt; component’s data option.</li><li>The code v-on:selectNote is creating an event listener on the &lt;note-container&gt; component. If the &lt;note-container&gt; component ever emits an event with the name selectNote, it will run whatever code it is being set equal to, which in this case is &quot;selectNote&quot;, a method that must be defined in the current &lt;App&gt; component’s methods option.</li></ul><p>This means the script in <strong>src/App.vue</strong> should define selectedNote in the data and selectNote in the methods, as follows:</p><pre>&lt;script&gt;<br>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;<br><br>export default {<br>  name: &#39;app&#39;,<br>  data: function() {<br><strong>    var initialNotes = [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ];<br>    return {<br>      notes: initialNotes,<br>      selectedNote: initialNotes[0]<br>    };</strong><br>  },<br>  methods: {<br><strong>    selectNote: function(note) {<br>      this.selectedNote = note;<br>    }</strong><br>  },<br>  components: {<br>    Toolbar,<br>    NoteContainer<br>  }<br>};<br>&lt;/script&gt;</pre><p>So far so good. Now we have to make sure the child &lt;note-container&gt; component will emit a selectNote event any time a note selector is clicked. This is what the <strong>src/components/NoteContainer.vue</strong> template and script look like:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-container&quot;&gt;<br>    &lt;note-selectors<br>      v-bind:notes=&quot;notes&quot;<br><strong>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:selectNote=&quot;selectNote&quot;</strong>&gt;<br>    &lt;/note-selectors&gt;<br>    &lt;note-editor<br>      v-bind:selectedNote=&quot;selectedNote&quot;&gt;<br>    &lt;/note-editor&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import NoteSelectors from &#39;./NoteSelectors&#39;;<br>import NoteEditor from &#39;./NoteEditor&#39;;<br><br>export default {<br>  name: &#39;note-container&#39;,<br>  <strong>props: [&#39;notes&#39;, &#39;selectedNote&#39;],</strong><br>  methods: {<br><strong>    selectNote: function(note) {<br>      this.$emit(&#39;selectNote&#39;, note);<br>    }</strong><br>  },<br>  components: {<br>    NoteSelectors,<br>    NoteEditor<br>  }<br>};<br>&lt;/script&gt;</pre><p>Similar to the &lt;App&gt; component, the &lt;note-container&gt; component has to pass the selectedNote prop and the selectNote event to the child &lt;note-selector&gt; component. Note that if the &lt;note-selector&gt; component emits a selectNote event, it will run this function:</p><pre>  methods: {<br>    selectNote: function(note) {<br>      this.$emit(&#39;selectNote&#39;, note);<br>    }<br>  },</pre><p>This function emits a selectNote event from the &lt;note-container&gt; component, which we set up an event listener in the parent to respond to. Also note that in the template, we’re sending the selectedNote prop to the &lt;note-editor&gt; component, which needs that info as well.</p><p>Now let’s take a look at the template and script in the &lt;note-selectors&gt; component in <strong>app/components/NoteSelectors.vue</strong>, which is what is actually process the click event:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-selectors&quot;&gt;<br>    &lt;note-selector<br>      v-for=&quot;note in transformedNotes&quot;<br>      v-bind:note=&quot;note&quot;<br>      <strong>v-bind:selectedNote=&quot;selectedNote&quot;</strong><br>      v-bind:key=&quot;note.id&quot;<br>      <strong>v-on:click.native=&quot;selectNote(note)&quot;</strong><br>    &gt;<br>    &lt;/note-selector&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import NoteSelector from &#39;./NoteSelector&#39;;<br><br>export default {<br>  name: &#39;note-selectors&#39;,<br>  <strong>props: [&#39;notes&#39;, &#39;selectedNote&#39;],</strong><br>  methods: {<br><strong>    selectNote: function(note) {<br>      this.$emit(&#39;selectNote&#39;, note);<br>    }</strong><br>  },<br>  computed: {<br>    transformedNotes: function() {<br>      return this.notes.slice().sort(function(a, b) {<br>        return b.timestamp - a.timestamp;<br>      });<br>    }<br>  },<br>  components: {<br>    NoteSelector<br>  }<br>};<br>&lt;/script&gt;</pre><p>The key line in the template is:</p><pre>      v-on:click.native=&quot;selectNote(note)&quot;</pre><p>This is binding the native DOM click event to a method called selectNote, which is defined in the component’s methods option:</p><pre>  methods: {<br>    selectNote: function(note) {<br>      this.$emit(&#39;selectNote&#39;, note);<br>    }<br>  },</pre><p>This starts the chain reaction — the user click’s on a &lt;note-selector&gt; component, triggering the parent &lt;note-selectors&gt; component’s selectNote method, which emits a &#39;selectNote&#39; event, triggering the parent &lt;note-container&gt; component’s selectNote method, which emits a &#39;selectNote&#39; event, triggering the parent &lt;App&gt; component’s selectNote method, which changes the actual selectedNote data. Once the parent’s data changes, Vue.js automatically re-renders all the appropriate child components.</p><p>This style of programming (props down, events up, made popular by <a href="https://facebook.github.io/react/">React</a>) seems overly verbose at first, but it’s the key to keeping the architecture of the code clean. Without it, we would be running into jQuery spaghetti territory. The reason we can avoid complexity is because each component is forced to only know about what it needs to know — a child component doesn’t manipulate the parent’s data directly, it simply passes a message to the parent telling it some event occurred.</p><p>Two final things to look at —first, the &lt;note-selector&gt; component should use the selectedNote prop to properly style the component if the note prop is the same as the selectedNote prop. The template and script in <strong>src/components/NoteSelector.vue</strong> would look as follows:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-selector&quot; <strong>v-bind:class=&quot;{active: note === selectedNote}&quot;</strong>&gt;<br>    &lt;p class=&quot;note-selector-title&quot;&gt;<br>      {{ note.body | formatTitle }}<br>    &lt;/p&gt;<br>    &lt;p class=&quot;note-selector-timestamp&quot;&gt;<br>      {{ note.timestamp | formatTimestamp }}<br>    &lt;/p&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>export default {<br>  name: &#39;note-selector&#39;,<br>  <strong>props: [&#39;note&#39;, &#39;selectedNote&#39;],</strong><br>  filters: {<br>    formatTitle: function(body) {<br>      var maxLength = 20;<br>      if (body.length &gt; maxLength) {<br>        return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>      } else if (body.length === 0) {<br>        return &quot;New note&quot;;<br>      } else {<br>        return body;<br>      }<br>    },<br>    formatTimestamp: function(timestamp) {<br>      return new Date(timestamp).toUTCString();<br>    }<br>  }<br>};<br>&lt;/script&gt;</pre><p>Here we’re using the v-bind:class directive to conditionally apply the .active class on the div. The second thing is that the &lt;note-editor&gt; component should also receive the selectedNote prop and display it’s info accordingly. Here’s the template and script for <strong>src/components/NoteEditor.vue</strong>:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-editor&quot;&gt;<br>    &lt;p class=&quot;note-editor-info&quot;&gt;<br>     <strong>{{ selectedNote.timestamp | formatTimestamp }}</strong><br>    &lt;/p&gt;<br>    &lt;textarea class=&quot;note-editor-input&quot;<br>      <strong>v-bind:value=&quot;selectedNote.body&quot;</strong>&gt;<br>    &lt;/textarea&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>export default {<br>  name: &#39;note-editor&#39;,<br>  <strong>props: [&#39;selectedNote&#39;]</strong><br>};<br>&lt;/script&gt;</pre><p>Here we’re registering &#39;selectedNote&#39; as a prop and binding selectedNote.body to the &lt;textarea&gt;. We also need to format the selectedNote.timestamp— this is a bit of a problem, since we specifically defined formatTimestamp as a filter in the &lt;note-selector&gt; component. Now we’ll need to define the method in a more general place for multiple components to be able to use it. This can be done by registering the filter in <strong>src/main.js</strong>, right before we define the new Vue instance, as follows:</p><pre>import Vue from &#39;vue&#39;<br>import App from &#39;./App.vue&#39;<br><br>Vue.config.productionTip = false<br><br><strong>Vue.filter(&#39;formatTimestamp&#39;, function(timestamp) {<br>  return new Date(timestamp).toUTCString();<br>});<br></strong><br>new Vue({<br>  render: h =&gt; h(App)<br>}).$mount(&#39;#app&#39;)</pre><p>That was a lot of code to make this particular feature work! You can see all the changes outlined above in this <a href="https://github.com/peterxjang/notes-app-vue-components/commit/ac117f3c372557f9562db4354a9327157d332e86">commit</a>.</p><h3>Edit the selected note on editor input</h3><p>Now we need to wire up the &lt;note-editor&gt; component to send events back up to the parent to edit the selectedNote’s body. Let’s look at it this time from the child going upwards, starting with the &lt;note-editor&gt; component in <strong>src/components/NoteEditor.vue</strong>:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-editor&quot;&gt;<br>    &lt;p class=&quot;note-editor-info&quot;&gt;<br>      {{ selectedNote.timestamp | formatTimestamp }}<br>    &lt;/p&gt;<br>    &lt;textarea class=&quot;note-editor-input&quot;<br>      v-bind:value=&quot;selectedNote.body&quot;<br>      <strong>v-on:input=&quot;input($event)&quot;</strong>&gt;<br>    &lt;/textarea&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>export default {<br>  name: &#39;note-editor&#39;,<br>  props: [&#39;selectedNote&#39;],<br>  methods: {<br><strong>    input: function($event) {<br>      this.$emit(&#39;inputNoteEditor&#39;, $event.target.value);<br>    }</strong><br>  }<br>};<br>&lt;/script&gt;</pre><p>Here the code v-on:input=&quot;input($event)&quot; is capturing the native DOM &lt;textarea&gt; input event and running the input method defined in the component’s methods option. The code this.$emit(&#39;inputNoteEditor&#39;, $event.target.value); will emit an &#39;inputNoteEditor&#39; event to the parent component, passing along the text from the &lt;textarea&gt;’s native DOM event. This means the parent &lt;note-container&gt; component must be wired up to listen for the &#39;inputNoteEditor&#39; event, which would change the code in <strong>src/components/NoteContainer.vue</strong> as follows:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-container&quot;&gt;<br>    &lt;note-selectors<br>      v-bind:notes=&quot;notes&quot;<br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:selectNote=&quot;selectNote&quot;&gt;<br>    &lt;/note-selectors&gt;<br>    &lt;note-editor<br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      <strong>v-on:inputNoteEditor=&quot;inputNoteEditor&quot;</strong>&gt;<br>    &lt;/note-editor&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import NoteSelectors from &#39;./NoteSelectors&#39;;<br>import NoteEditor from &#39;./NoteEditor&#39;;<br><br>export default {<br>  name: &#39;note-container&#39;,<br>  props: [&#39;notes&#39;, &#39;selectedNote&#39;],<br>  methods: {<br>    selectNote: function(note) {<br>      this.$emit(&#39;selectNote&#39;, note);<br>    },<br><strong>    inputNoteEditor: function(body) {<br>      this.$emit(&#39;inputNoteEditor&#39;, body);<br>    }</strong><br>  },<br>  components: {<br>    NoteSelectors,<br>    NoteEditor<br>  }<br>};<br>&lt;/script&gt;</pre><p>The code v-on:inputNoteEditor=&quot;inputNoteEditor&quot; is listening for inputNoteEditor events from the child &lt;note-editor&gt; component, which will run the inputNoteEditor method defined in the methods option. The code this.$emit(&#39;inputNoteEditor&#39;, body); in the inputNoteEditor method will simply bubble the information further to the parent. Finally, the parent component will receive the data and update the selectedNote, which you can see in <strong>src/App.vue</strong>:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br>    &lt;toolbar&gt;&lt;/toolbar&gt;<br>    &lt;note-container<br>      v-bind:notes=&quot;notes&quot;<br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:selectNote=&quot;selectNote&quot;<br>      <strong>v-on:inputNoteEditor=&quot;updateSelectedNote&quot;</strong><br>    &gt;<br>    &lt;/note-container&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;<br><br>export default {<br>  name: &#39;app&#39;,<br>  data: function() {<br>    var initialNotes = [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ];<br>    return {<br>      notes: initialNotes,<br>      selectedNote: initialNotes[0]<br>    };<br>  },<br>  methods: {<br>    selectNote: function(note) {<br>      this.selectedNote = note;<br>    },<br><strong>    updateSelectedNote: function(body) {<br>      this.selectedNote.body = body;<br>      this.selectedNote.timestamp = Date.now();<br>    }</strong><br>  },<br>  components: {<br>    Toolbar,<br>    NoteContainer<br>  }<br>};<br>&lt;/script&gt;</pre><p>The code v-on:inputNoteEditor=&quot;updateSelectedNote&quot; is listening for inputNoteEditor events from the &lt;note-container&gt; component, and will run the updateSelectedNote method defined in the methods option accordingly. The updateSelectedNote method simply updates the selectedNote’s body and timestamp, which finally automatically trigger’s Vue.js to re-render all the child components.</p><h3>Create a new note with a button</h3><p>Now let’s implement the ability to create a new note. Clicking on the “New” button should create a new note (new id, no body, current timestamp). The new note should become the currently selected note and appear at the top of the list of note selectors. Let’s start with the child &lt;toolbar&gt; component in <strong>src/components/Toolbar.vue</strong>:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;toolbar&quot;&gt;<br>    &lt;button class=&quot;toolbar-button&quot; <strong>v-on:click=&quot;clickNew&quot;</strong>&gt;<br>      New<br>    &lt;/button&gt;<br>    &lt;button class=&quot;toolbar-button&quot;&gt;Delete&lt;/button&gt;<br>    &lt;input class=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot;&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>export default {<br>  name: &#39;toolbar&#39;,<br>  methods: {<br><strong>    clickNew: function() {<br>      this.$emit(&#39;clickNew&#39;);<br>    }</strong><br>  }<br>};<br>&lt;/script&gt;</pre><p>Here the code v-on:click=&quot;clickNew&quot; is capturing the native DOM &lt;button&gt; click event and running the clickNew method defined in the component’s methods option. The code this.$emit(&#39;clickNew&#39;); will emit an &#39;clickNew&#39; event to the parent component. This means the parent &lt;App&gt; component must be wired up to listen for the &#39;clickNew&#39; event, which would change the code in <strong>src/App.vue</strong> as follows:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br>    &lt;toolbar <strong>v-on:clickNew=&quot;createNote&quot;</strong>&gt;&lt;/toolbar&gt;<br>    &lt;note-container<br>      v-bind:notes=&quot;notes&quot;<br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:selectNote=&quot;selectNote&quot;<br>      v-on:inputNoteEditor=&quot;updateSelectedNote&quot;<br>    &gt;<br>    &lt;/note-container&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;<br><br>export default {<br>  name: &#39;app&#39;,<br>  data: function() {<br>    var initialNotes = [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ];<br>    return {<br>      notes: initialNotes,<br>      selectedNote: initialNotes[0]<br>    };<br>  },<br>  methods: {<br>    selectNote: function(note) {<br>      this.selectedNote = note;<br>    },<br>    updateSelectedNote: function(body) {<br>      this.selectedNote.body = body;<br>      this.selectedNote.timestamp = Date.now();<br>    },<br><strong>    createNote: function() {<br>      var newNote = {<br>        id: Date.now(),<br>        body: &quot;&quot;,<br>        timestamp: Date.now()<br>      };<br>      this.notes.push(newNote);<br>      this.selectedNote = newNote;<br>    }</strong><br>  },<br>  components: {<br>    Toolbar,<br>    NoteContainer<br>  }<br>};<br>&lt;/script&gt;</pre><p>The code v-on:clickNew=&quot;createNote&quot; is listening for clickNew events from the &lt;toolbar&gt; component, and will run the createNote method defined in the methods option accordingly. The createNote method pushes a new empty note into the notes data as well as updates the selectedNote data to the new note, which finally automatically trigger’s Vue.js to re-render all the child components.</p><h3>Delete the selected note with a button</h3><p>Wiring up this feature starts off pretty similar to the new note feature. Let’s again start with the child &lt;toolbar&gt; component in <strong>src/components/Toolbar.vue</strong>:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;toolbar&quot;&gt;<br>    &lt;button class=&quot;toolbar-button&quot; v-on:click=&quot;clickNew&quot;&gt;<br>      New<br>    &lt;/button&gt;<br>    &lt;button class=&quot;toolbar-button&quot; <strong>v-on:click=&quot;clickDelete&quot;</strong>&gt;<br>      Delete<br>    &lt;/button&gt;<br>    &lt;input class=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot;&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>export default {<br>  name: &#39;toolbar&#39;,<br>  methods: {<br>    clickNew: function() {<br>      this.$emit(&#39;clickNew&#39;);<br>    },<br><strong>    clickDelete: function() {<br>      this.$emit(&#39;clickDelete&#39;);<br>    }</strong><br>  }<br>};<br>&lt;/script&gt;</pre><p>Here the code v-on:click=&quot;clickDelete&quot; is capturing the native DOM &lt;button&gt; click event and running the clickDelete method defined in the component’s methods option. The code this.$emit(&#39;clickDelete&#39;); will emit an &#39;clickDelete&#39; event to the parent component. This means the parent &lt;App&gt; component must be wired up to listen for the &#39;clickDelete&#39; event, which would change the code in <strong>src/App.vue</strong> as follows:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br>    &lt;toolbar<br>      v-on:clickNew=&quot;createNote&quot;<br>      <strong>v-on:clickDelete=&quot;deleteNote&quot;</strong>&gt;<br>    &lt;/toolbar&gt;<br>    &lt;note-container<br>      v-bind:notes=&quot;notes&quot;<br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:selectNote=&quot;selectNote&quot;<br>      v-on:inputNoteEditor=&quot;updateSelectedNote&quot;<br>    &gt;<br>    &lt;/note-container&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;<br><br>export default {<br>  name: &#39;app&#39;,<br>  data: function() {<br>    var initialNotes = [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ];<br>    return {<br>      notes: initialNotes,<br>      selectedNote: initialNotes[0]<br>    };<br>  },<br>  methods: {<br>    selectNote: function(note) {<br>      this.selectedNote = note;<br>    },<br>    updateSelectedNote: function(body) {<br>      this.selectedNote.body = body;<br>      this.selectedNote.timestamp = Date.now();<br>    },<br>    createNote: function() {<br>      var newNote = {<br>        id: Date.now(),<br>        body: &quot;&quot;,<br>        timestamp: Date.now()<br>      };<br>      this.notes.push(newNote);<br>      this.selectedNote = newNote;<br>    },<br><strong>    deleteNote: function() {<br>      var index = this.notes.indexOf(this.selectedNote);<br>      if (index !== -1) {<br>        this.notes.splice(index, 1);<br>        if (this.notes.length &gt; 0) {<br>          this.selectedNote = this.notes[0];<br>        } else {<br>          this.selectedNote = {};<br>        }<br>      }<br>    }</strong><br>  },<br>  components: {<br>    Toolbar,<br>    NoteContainer<br>  }<br>};<br>&lt;/script&gt;</pre><p>The code v-on:clickDelete=&quot;deleteNote&quot; is listening for clickDelete events from the &lt;toolbar&gt; component, and will run the deleteNote method defined in the methods option accordingly. The deleteNote method deletes the selected note from the notes data, which finally automatically trigger’s Vue.js to re-render all the child components.</p><p>However, there’s a problem — the deleteNote method also needs to select a new note in place of the one that’s deleted. The problem is that selecting a new note means we need to know the top of the list of the <strong>transformed</strong> (sorted) notes, which we defined in the sub-child &lt;note-selectors&gt; component. That seemed like the right decision at the time, but now we’ll need to refactor that method to be in the parent component, then pass the transformed notes as a prop to the appropriate children. This would change the code in <strong>src/App.vue</strong> as follows:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br>    &lt;toolbar<br>      v-on:clickNew=&quot;createNote&quot;<br>      v-on:clickDelete=&quot;deleteNote&quot;&gt;<br>    &lt;/toolbar&gt;<br>    &lt;note-container<br>      v-bind:notes=&quot;notes&quot;<br>      <strong>v-bind:transformedNotes=&quot;transformedNotes&quot;</strong><br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:selectNote=&quot;selectNote&quot;<br>      v-on:inputNoteEditor=&quot;updateSelectedNote&quot;<br>    &gt;<br>    &lt;/note-container&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;<br><br>export default {<br>  name: &#39;app&#39;,<br>  data: function() {<br>    var initialNotes = [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ];<br>    return {<br>      notes: initialNotes,<br>      selectedNote: initialNotes[0]<br>    };<br>  },<br>  methods: {<br>    selectNote: function(note) {<br>      this.selectedNote = note;<br>    },<br>    updateSelectedNote: function(body) {<br>      this.selectedNote.body = body;<br>      this.selectedNote.timestamp = Date.now();<br>    },<br>    createNote: function() {<br>      var newNote = {<br>        id: Date.now(),<br>        body: &quot;&quot;,<br>        timestamp: Date.now()<br>      };<br>      this.notes.push(newNote);<br>      this.selectedNote = newNote;<br>    },<br>    deleteNote: function() {<br>      var index = this.notes.indexOf(this.selectedNote);<br>      if (index !== -1) {<br>        this.notes.splice(index, 1);<br>        if (<strong>this.transformedNotes</strong>.length &gt; 0) {<br>          this.selectedNote = <strong>this.transformedNotes</strong>[0];<br>        } else {<br>          this.selectedNote = {};<br>        }<br>      }<br>    }<br>  },<br><strong>  computed: {<br>    transformedNotes: function() {<br>      return this.notes.slice().sort(function(a, b) {<br>        return b.timestamp - a.timestamp;<br>      });<br>    }<br>  },</strong><br>  components: {<br>    Toolbar,<br>    NoteContainer<br>  }<br>};<br>&lt;/script&gt;</pre><p>The code in <strong>src/components/NoteContainer.vue</strong> would simply pass the transformedNotes props down as follows:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-container&quot;&gt;<br>    &lt;note-selectors<br>      v-bind:notes=&quot;notes&quot;<br>      <strong>v-bind:transformedNotes=&quot;transformedNotes&quot;</strong><br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:selectNote=&quot;selectNote&quot;&gt;<br>    &lt;/note-selectors&gt;<br>    &lt;note-editor<br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:inputNoteEditor=&quot;inputNoteEditor&quot;&gt;<br>    &lt;/note-editor&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import NoteSelectors from &#39;./NoteSelectors&#39;;<br>import NoteEditor from &#39;./NoteEditor&#39;;<br><br>export default {<br>  name: &#39;note-container&#39;,<br>  props: [&#39;notes&#39;, <strong>&#39;transformedNotes&#39;</strong>, &#39;selectedNote&#39;],<br>  methods: {<br>    selectNote: function(note) {<br>      this.$emit(&#39;selectNote&#39;, note);<br>    },<br>    inputNoteEditor: function(body) {<br>      this.$emit(&#39;inputNoteEditor&#39;, body);<br>    }<br>  },<br>  components: {<br>    NoteSelectors,<br>    NoteEditor<br>  }<br>};<br>&lt;/script&gt;</pre><p>And finally, the code in <strong>src/components/NoteSelectors.vue</strong> would no longer compute the transformedNotes but instead would use the transformedNotes props as follows:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-selectors&quot;&gt;<br>    &lt;note-selector<br>      <strong>v-for=&quot;note in transformedNotes&quot;</strong><br>      v-bind:note=&quot;note&quot;<br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-bind:key=&quot;note.id&quot;<br>      v-on:click.native=&quot;selectNote(note)&quot;<br>    &gt;<br>    &lt;/note-selector&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import NoteSelector from &#39;./NoteSelector&#39;;<br><br>export default {<br>  name: &#39;note-selectors&#39;,<br>  props: [&#39;notes&#39;, <strong>&#39;transformedNotes&#39;</strong>, &#39;selectedNote&#39;],<br>  methods: {<br>    selectNote: function(note) {<br>      this.$emit(&#39;selectNote&#39;, note);<br>    }<br>  },<br>  components: {<br>    NoteSelector<br>  }<br>};<br>&lt;/script&gt;</pre><p>The last detail is to change the &lt;note-editor&gt; component to only render if there is a selectedNote, since it’s now possible to delete all the notes. The template in <strong>src/components/NoteEditor.vue</strong> would change as follows:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;note-editor&quot; <strong>v-if=&quot;selectedNote.id&quot;</strong>&gt;<br>    &lt;p class=&quot;note-editor-info&quot;&gt;<br>      {{ selectedNote.timestamp | formatTimestamp }}<br>    &lt;/p&gt;<br>    &lt;textarea class=&quot;note-editor-input&quot;<br>      v-bind:value=&quot;selectedNote.body&quot;<br>      v-on:input=&quot;input($event)&quot;&gt;<br>    &lt;/textarea&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;</pre><h3>Filter notes on search input</h3><p>The final feature is to be able to search notes immediately as you type in the search input. This means we’ll need to keep track of a new piece of data — the search text the user enters. This search text will be used to filter the list of notes, which means it must be defined in the &lt;App&gt; component, since that’s the component that stores the notes. The code in <strong>src/App.vue</strong> would change as follows:</p><pre>&lt;template&gt;<br>  &lt;div id=&quot;app&quot;&gt;<br>    &lt;toolbar<br>      v-on:clickNew=&quot;createNote&quot;<br>      v-on:clickDelete=&quot;deleteNote&quot;<br>      <strong>v-bind:searchNoteText=&quot;searchNoteText&quot;</strong><br>      <strong>v-on:inputSearchNoteText=&quot;updateSearch&quot;</strong><br>    &gt;<br>    &lt;/toolbar&gt;<br>    &lt;note-container<br>      v-bind:notes=&quot;notes&quot;<br>      v-bind:transformedNotes=&quot;transformedNotes&quot;<br>      v-bind:selectedNote=&quot;selectedNote&quot;<br>      v-on:selectNote=&quot;selectNote&quot;<br>      v-on:inputNoteEditor=&quot;updateSelectedNote&quot;<br>    &gt;<br>    &lt;/note-container&gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>import Toolbar from &#39;./components/Toolbar&#39;;<br>import NoteContainer from &#39;./components/NoteContainer&#39;;<br><br>export default {<br>  name: &#39;app&#39;,<br>  data: function() {<br>    var initialNotes = [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ];<br>    return {<br>      notes: initialNotes,<br>      selectedNote: initialNotes[0],<br>      <strong>searchNoteText: &quot;&quot;</strong><br>    };<br>  },<br>  methods: {<br>    selectNote: function(note) {<br>      this.selectedNote = note;<br>    },<br>    updateSelectedNote: function(body) {<br>      this.selectedNote.body = body;<br>      this.selectedNote.timestamp = Date.now();<br>    },<br>    createNote: function() {<br>      var newNote = {<br>        id: Date.now(),<br>        body: &quot;&quot;,<br>        timestamp: Date.now()<br>      };<br>      this.notes.push(newNote);<br>      this.selectedNote = newNote;<br>    },<br>    deleteNote: function() {<br>      var index = this.notes.indexOf(this.selectedNote);<br>      if (index !== -1) {<br>        this.notes.splice(index, 1);<br>        if (this.transformedNotes.length &gt; 0) {<br>          this.selectedNote = this.transformedNotes[0];<br>        } else {<br>          this.selectedNote = {};<br>        }<br>      }<br>    },<br><strong>    updateSearch: function(newSearchText) {<br>      this.searchNoteText = newSearchText;<br>      if (this.transformedNotes.length === 0) {<br>        this.selectedNote = {};<br>      } else if (this.transformedNotes.indexOf(this.selectedNote) === -1) {<br>        this.selectedNote = this.transformedNotes[0];<br>      }<br>    }</strong><br>  },<br>  computed: {<br><strong>    transformedNotes: function() {<br>      return this.notes<br>        .filter(function(note) {<br>          return note.body.toLowerCase().indexOf(this.searchNoteText.toLowerCase()) !== -1;<br>        }.bind(this))<br>        .sort(function(a, b) {<br>          return b.timestamp - a.timestamp;<br>        });<br>    }</strong><br>  },<br>  components: {<br>    Toolbar,<br>    NoteContainer<br>  }<br>};<br>&lt;/script&gt;</pre><p>Let’s go through these changes one at a time.</p><ul><li>The code v-bind:searchNoteText=&quot;searchNoteText&quot; is sending the searchNoteText data to the &lt;toolbar&gt; component as a prop named searchNoteText.</li><li>The code v-on:inputSearchNoteText=&quot;updateSearch&quot; is listening for inputSearchNoteText events from the &lt;toolbar&gt; component, and will run the updateSearch method defined in the methods option accordingly.</li><li>The updateSearch method updates the searchNoteText data to the input argument, which automatically trigger’s Vue.js to re-render all the child components. It also has to update the selectedNote to be the top of the transformedNotes if possible.</li><li>The transformedNotes computed property, which used to simply sort the notes, is now responsible for both filtering and sorting the notes. The notes are filtered based on whether or not the body contains the searchNoteText data.</li></ul><p>Finally, we need to wire the child &lt;toolbar&gt; component in <strong>src/components/Toolbar.vue</strong> to respond to and fire off the right events:</p><pre>&lt;template&gt;<br>  &lt;div class=&quot;toolbar&quot;&gt;<br>    &lt;button class=&quot;toolbar-button&quot; v-on:click=&quot;clickNew&quot;&gt;<br>      New<br>    &lt;/button&gt;<br>    &lt;button class=&quot;toolbar-button&quot; v-on:click=&quot;clickDelete&quot;&gt;<br>      Delete<br>    &lt;/button&gt;<br>    &lt;input class=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot;<br>      <strong>v-bind:value=&quot;searchNoteText&quot;</strong><br>      <strong>v-on:input=&quot;inputSearchNoteText($event)&quot;</strong><br>    &gt;<br>  &lt;/div&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>export default {<br>  name: &#39;toolbar&#39;,<br>  props: [<strong>&#39;searchNoteText&#39;</strong>],<br>  methods: {<br>    clickNew: function() {<br>      this.$emit(&#39;clickNew&#39;);<br>    },<br>    clickDelete: function() {<br>      this.$emit(&#39;clickDelete&#39;);<br>    },<br><strong>    inputSearchNoteText: function($event) {<br>      this.$emit(&#39;inputSearchNoteText&#39;, $event.target.value);<br>    }</strong><br>  }<br>};<br>&lt;/script&gt;</pre><p>As seen before, the &lt;toolbar&gt; component contains no logic — it simply receives props and sends messages to the parent when the user interacts with it in various ways.</p><p>You can take a look at the final app in all its glory in this <a href="https://github.com/peterxjang/notes-app-vue-components/tree/be29b712ccfe8e45be80806eba30f3d004057333">GitHub repository</a>.</p><h3>Conclusion</h3><p>As you can see, working with components is incredibly different than without, even when using the same Vue.js framework! This is truly what Vue.js means when it describes itself as a progressive framework — you don’t need to understand components to get started with it, which simplifies the on boarding learning curve. For larger scale apps, it can be worthwhile to learn this component architecture as a solid way to organize your code.</p><p>I would argue that for this particular notes app, a Vue.js component architecture may be overkill. However, this is a debatable point, since every app continues to grow in complexity as more features are added. Still, even though components are daunting at first, you start to get used to the repeating patterns and they become easy enough over time. Only when your app grows so large that it becomes nearly impossible to understand all the code at once will you appreciate the clean separations that a component architecture provides.</p><p>In the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-5-react-b51fd7d075fe">next part</a> of this series, we’ll take a look at implementing the same notes app using <a href="https://facebook.github.io/react/">React</a>, a completely different framework with a similar component model. Stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=675c880d4585" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Comparing Frontend Approaches Part 4: Vue.js with Components</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Comparing Frontend Approaches Part 3: Vue.js]]></title>
            <link>https://medium.com/actualize-network/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/8b8614e4f324</guid>
            <category><![CDATA[vuejs]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Sat, 13 Jan 2018 23:13:58 GMT</pubDate>
            <atom:updated>2019-04-09T02:23:58.769Z</atom:updated>
            <content:encoded><![CDATA[<h4>A look at jQuery, Vue.js, React, and Elm</h4><ul><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">Part 1: Introduction</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">Part 2: jQuery</a></li><li><strong>Part 3: Vue.js</strong></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Part 4: Vue.js with components</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-5-react-b51fd7d075fe">Part 5: React</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-6-elm-578714526164">Part 6: Elm</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-approaches-part-7-final-thoughts-69cdba516f86">Part 7: Final thoughts</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/239/1*ehZx_7oYWnPmyy-tdWIo5Q.png" /></figure><p>In this part we will be implementing the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">web based clone</a> of the <a href="https://support.apple.com/kb/PH22608?locale=en_US">Mac Notes app</a> using <a href="https://vuejs.org/">Vue.js</a>. Vue.js is a JavaScript framework that came out in 2014 and has recently become one of the most popular frameworks in a very crowded market. It currently has over 65,000 stars on GitHub, which makes it the second most popular JavaScript framework (React being the first).</p><p>Vue.js uses a template based approach, which was heavily inspired by <a href="https://angularjs.org/">Angular 1</a> (which was once the dominant framework). It also uses a virtual DOM under the hood, which was inspired by React. Vue.js labels itself as “the progressive JavaScript framework”, which means you don’t need to learn the entire framework to get started — you start with the basics and add on more advanced features as they become necessary. Let’s see what it’s like to get started with it in practice!</p><blockquote>Note — this isn’t meant to be a tutorial for people completely new to Vue.js. If you are in that category, I would recommend reading through at least the beginning of the <a href="https://vuejs.org/v2/guide/">official guide</a> (which is very well written and beginner friendly) before reading through this post.</blockquote><h3>Installation</h3><p>Starting from our <a href="https://jsfiddle.net/peterxjang/vtsjc5w9">initial template</a> in <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">part 1</a>, installing Vue.js is easy enough (almost as easy as installing <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">jQuery</a>). Here are the two changes in the HTML head tag:</p><pre>&lt;script src=&quot;https://unpkg.com/vue/dist/vue.js&quot;&gt;&lt;/script&gt;<br>&lt;script src=&quot;js/notes.js&quot; defer&gt;&lt;/script&gt;</pre><p>The first line includes Vue.js from a CDN. The second line includes the app’s JavaScript and only has one change from the original template — the defer attribute. This ensures that the notes.js file is loaded only after the DOM is ready. In the notes.js file we need the following initial code:</p><pre>var app = new Vue({<br>  el: &#39;#app&#39;<br>});</pre><p>This instantiates the Vue instance — pretty much all of our JavaScript will be written and organized within this object. The el option has a value of &#39;#app&#39;, which is the id of the HTML div where Vue.js will bind itself to.</p><h3>Display note titles from an array of notes</h3><p>Now that Vue.js is installed, let’s change the starting template to generate the dynamic notes list using JavaScript.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2FxjhLhs4u%2Fembedded%2F&amp;url=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2FxjhLhs4u%2F&amp;image=https%3A%2F%2Fwww.gravatar.com%2Favatar%2Fbe58ab79b144e42a4cc9300d5206c06d%2F%3Fdefault%3D%26s%3D80&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=jsfiddle" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/8505578589708dd88885841b90956e20/href">https://medium.com/media/8505578589708dd88885841b90956e20/href</a></iframe><p>In Vue.js, we will store any application state with an object in the data option. Here I’m representing the notes as an array of objects, where each object has an id, a body, and a timestamp. The Date.now() returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC.</p><pre>  data: {<br>    notes: [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ]<br>  }</pre><p>Once we have the notes in the data, we can bind them to the DOM using Vue’s template syntax directly in the HTML:</p><pre>&lt;div class=&quot;note-selectors&quot;&gt;<br>  &lt;div class=&quot;note-selector&quot; v-for=&quot;note in notes&quot;&gt;<br>    &lt;p class=&quot;note-selector-title&quot;&gt;{{ note.body }}&lt;/p&gt;<br>    &lt;p class=&quot;note-selector-timestamp&quot;&gt;{{ note.timestamp }}&lt;/p&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><p>It looks like there’s only a single .note-selector div here, but the v-for directive is Vue specific syntax that will repeat the div for each note in the notes array that was defined in the Vue instance. Within any element content you can write JavaScript within {{ }}, which is capable of referencing data defined in the Vue instance data option.</p><h3>Use computed properties and filters to sort and format notes</h3><p>Similar to the jQuery example, we’ll make helper methods to make sure the notes are sorted (newest first) and formatted properly (titles should be computed from the body, timestamps should be converted from milliseconds into a human readable string). Unlike jQuery, Vue.js gives a structure to organize where such methods can go — in the methods option:</p><pre>var app = new Vue({<br>  el: &#39;#app&#39;,<br>  data: {<br>    notes: [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ]<br>  },<br>  methods: {<br>    transformNotes: function(notes) {<br>      return notes.slice().sort(function(a, b) {<br>        return b.timestamp - a.timestamp;<br>      });<br>    },<br>    formatTitle: function(body) {<br>      var maxLength = 20;<br>      if (body.length &gt; maxLength) {<br>        return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>      } else if (body.length === 0) {<br>        return &quot;New note&quot;;<br>      } else {<br>        return body;<br>      }<br>    },<br>    formatTimestamp: function(timestamp) {<br>      return new Date(timestamp).toUTCString();<br>    }<br>  }<br>});</pre><p>To use these methods, you would change the HTML template as follows:</p><pre>&lt;div class=&quot;note-selectors&quot;&gt;<br>  &lt;div class=&quot;note-selector&quot; v-for=&quot;note in transformNotes(notes)&quot;&gt;<br>    &lt;p class=&quot;note-selector-title&quot;&gt;<br>      {{ formatTitle(note.body) }}<br>    &lt;/p&gt;<br>    &lt;p class=&quot;note-selector-timestamp&quot;&gt;<br>      {{ formatTimestamp(note.timestamp) }}<br>    &lt;/p&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><p>This works great, but Vue.js actually provides other options besides methods to work with data to make it both more readable and performant. One option is defining computed properties, which essentially creates new data available in the template that is computed based on existing data. In this case we can refactor the transformNotes function in the methods option into a transformedNotes function in the computed option:</p><pre>var app = new Vue({<br>  el: &#39;#app&#39;,<br>  data: {<br>    notes: [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ]<br>  },<br>  computed: {<br>    transformedNotes: function() {<br>      return this.notes.slice().sort(function(a, b) {<br>        return b.timestamp - a.timestamp;<br>      });<br>    }<br>  },<br>  methods: {<br>    formatTitle: function(body) {<br>      var maxLength = 20;<br>      if (body.length &gt; maxLength) {<br>        return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>      } else if (body.length === 0) {<br>        return &quot;New note&quot;;<br>      } else {<br>        return body;<br>      }<br>    },<br>    formatTimestamp: function(timestamp) {<br>      return new Date(timestamp).toUTCString();<br>    }<br>  }<br>});</pre><p>Note the use of this.notes in the transformNotes function — any function within the Vue.js instance can refer to any value from the data object using this. The HTML template would refer to the computed property transformedNotes instead of the method transformNotes(notes) as follows:</p><pre>  &lt;div class=&quot;note-selector&quot; v-for=&quot;note in transformedNotes&quot;&gt;</pre><p>At a glance, using computed properties aren’t too different from using methods. The reason they’re valuable is that methods will be called whenever a re-render occurs, whereas a computed property can be cached and will only be recomputed when the underlying data changes (which Vue manages for you).</p><p>Vue.js also provides a mechanism called filters, which can be used for simple text formatting. That’s exactly what we’re doing with the formatTitle and formatTimestamp methods, so let’s see what they would look like using filters instead:</p><pre>var app = new Vue({<br>  el: &#39;#app&#39;,<br>  data: {<br>    notes: [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ]<br>  },<br>  computed: {<br>    transformedNotes: function() {<br>      return this.notes.slice().sort(function(a, b) {<br>        return b.timestamp - a.timestamp;<br>      });<br>    }<br>  },<br>  filters: {<br>    formatTitle: function(body) {<br>      var maxLength = 20;<br>      if (body.length &gt; maxLength) {<br>        return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>      } else if (body.length === 0) {<br>        return &quot;New note&quot;;<br>      } else {<br>        return body;<br>      }<br>    },<br>    formatTimestamp: function(timestamp) {<br>      return new Date(timestamp).toUTCString();<br>    }<br>  },<br>  methods: {</pre><pre>  }<br>});</pre><p>And the HTML template would change as follows:</p><pre>&lt;div class=&quot;note-selectors&quot;&gt;<br>  &lt;div class=&quot;note-selector&quot; v-for=&quot;note in transformedNotes&quot;&gt;<br>    &lt;p class=&quot;note-selector-title&quot;&gt;<br>      {{ note.body | formatTitle }}&lt;/p&gt;<br>    &lt;p class=&quot;note-selector-timestamp&quot;&gt;<br>      {{ note.timestamp | formatTimestamp }}<br>    &lt;/p&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><p>I would say that when you’re first starting out with Vue.js, it may be helpful just to stick with methods instead of worrying about computed properties and filters, since you can pull off the same functionality. It’s nice to be aware of them as potential tools if you find yourself in need of better code organization or performance!</p><h3>Select a note on title click</h3><p>Now let’s implement the ability to actually select notes. Clicking on a note title should both highlight the selected note on the left as well as display the contents in the editor on the right. Here’s a complete working example, which we’ll go over in detail:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2F7dep6e47%2Fembedded%2F&amp;url=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2F7dep6e47%2F&amp;image=https%3A%2F%2Fwww.gravatar.com%2Favatar%2Fbe58ab79b144e42a4cc9300d5206c06d%2F%3Fdefault%3D%26s%3D80&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=jsfiddle" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/7d49c572ca1beeb1f74e79a9619f9edc/href">https://medium.com/media/7d49c572ca1beeb1f74e79a9619f9edc/href</a></iframe><p>First we need to keep track of a new variable selectedNote (which we’ll default to the first note). To do so, we’ll move the initialization of the data into a new mounted option, which is one of <a href="https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks">several lifecycle hooks</a> available. The mounted function runs once when everything is ready in the DOM:</p><pre>  data: {<br>    notes: [],<br>    selectedNote: {}<br>  },<br>  mounted: function() {<br>    this.notes = [<br>      {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>      {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>      {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>    ];<br>    this.selectedNote = this.notes[0];<br>  },</pre><p>Note that Vue.js requires all data that will be referred to an HTML template to be defined in the data object — this is why I’m defining notes as an empty array and selectedNote as an empty object. In the mounted function, I’m putting in the actual data. This is good organization for later, where we can replace the hard coded data with a web request from a backend for real notes data.</p><p>Now let’s take a look at the changes in the HTML template:</p><pre>&lt;div class=&quot;note-selectors&quot;&gt;<br>  &lt;div class=&quot;note-selector&quot;<br>    v-for=&quot;note in transformedNotes&quot;<br>    v-on:click=&quot;selectNote(note)&quot;<br>    v-bind:class=&quot;{active: note === selectedNote}&quot;<br>  &gt;<br>    &lt;p class=&quot;note-selector-title&quot;&gt;<br>      {{ note.body | formatTitle }}<br>    &lt;/p&gt;<br>    &lt;p class=&quot;note-selector-timestamp&quot;&gt;<br>      {{ note.timestamp | formatTimestamp }}<br>    &lt;/p&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><p>We have two new lines — first is v-on:click=&quot;selectNote(note)&quot;, which essentially works as a DOM event handler and will run the selectNote method when the div is clicked. We haven’t defined this method yet, we’ll look at that in a bit. The second new line is v-bind:class=&quot;{active: note === selectedNote}&quot;, which will conditionally add the active class to the div (depending on whether or not the particular note equals the selected note from Vue’s data). It’s important to note that with Vue.js, you must use v-bind with any HTML attributes that you need to be dynamic.</p><blockquote>Side note — Vue.js provides shortcuts for v-on and v-bind — v-on:click becomes @click and v-bind:class becomes :class. I’ll avoid using the shortcuts for the sake of being explicit, but it’s good to know about them when writing and reading other Vue.js code.</blockquote><p>The selectNote method is easy enough to define:</p><pre>  methods: {<br>    selectNote: function(note) {<br>      this.selectedNote = note;<br>    }<br>  }</pre><p>At this point everything works automagically — clicking on a note will run the selectNote function, which updates this.selectedNote, which will trigger a re-render, which will bind the active class to the appropriate div. This is the promise of modern JavaScript frameworks — your job as a programmer is to define data and the ways it gets transformed. The framework’s job is to manage and manipulate the DOM under the hood.</p><p>The last bit here is to update the note editor on the right to display the selected note dynamically. Here’s the HTML template needed to do so:</p><pre>&lt;div class=&quot;note-editor&quot;&gt;<br>  &lt;p class=&quot;note-editor-info&quot;&gt;<br>    {{ selectedNote.timestamp | formatTimestamp }}<br>  &lt;/p&gt;<br>  &lt;textarea class=&quot;note-editor-input&quot;&gt;<br>    {{ selectedNote.body }}<br>  &lt;/textarea&gt;<br>&lt;/div&gt;</pre><p>Since the template is referring to selectedNote, which is defined and managed in the Vue instance, no other work is necessary!</p><h3>Edit the selected note on editor input</h3><p>Next we’ll bind the &lt;textarea&gt; to the note data. Vue.js has this functionality built in with the v-model directive:</p><pre>&lt;div class=&quot;note-editor&quot;&gt;<br>  &lt;p class=&quot;note-editor-info&quot;&gt;<br>    {{ selectedNote.timestamp | formatTimestamp }}<br>  &lt;/p&gt;<br>  &lt;textarea class=&quot;note-editor-input&quot; v-model=&quot;selectedNote.body&quot;&gt;<br>  &lt;/textarea&gt;<br>&lt;/div&gt;</pre><p>(Note that we’re no longer putting content between the &lt;textarea&gt;&lt;/textarea&gt; tags, the Vue.js v-model directive is handling it now.) Now whenever the user enters text, the selectedNote’s body automatically gets updated! Now the only thing missing is the selectedNote’s timestamp — it should get updated to the current time whenever the text changes. We can accomplish this using the watch option:</p><pre>var app = new Vue({<br>  el: &#39;#app&#39;,<br>  data: {<br>    // ...<br>  },<br>  mounted: function() {<br>    // ...<br>  },<br>  computed: {<br>    // ...<br>  },<br>  filters: {<br>    // ...<br>  },<br>  methods: {<br>    // ...<br>  },<br>  watch: {<br>    notes: {<br>      handler: function() {<br>        this.selectedNote.timestamp = Date.now();<br>      },<br>      deep: true<br>    }<br>  }<br>});</pre><p>This is telling Vue.js to watch the notes data for any changes and run the code to update the selectedNote’s timestamp whenever something does change. (It seems a bit strange to watch notes instead of selectedNote, but see what happens if you change it to selectedNote — each time the user selects a new note, its timestamp gets updated, which is not the desired behavior!)</p><h3>Create a new note with a button</h3><p>Now let’s implement the ability to create a new note. Clicking on the “New” button should create a new note (new id, no body, current timestamp). The new note should become the currently selected note and appear at the top of the list of note selectors. Here’s a complete working example, which we’ll go over in detail:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2F9kLqrg2n%2Fembedded%2F&amp;url=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2F9kLqrg2n%2F&amp;image=https%3A%2F%2Fwww.gravatar.com%2Favatar%2Fbe58ab79b144e42a4cc9300d5206c06d%2F%3Fdefault%3D%26s%3D80&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=jsfiddle" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/2980862d10534868ca5f12dfbee7f417/href">https://medium.com/media/2980862d10534868ca5f12dfbee7f417/href</a></iframe><p>There’s actually very little that’s changed here to make this feature work! In the HTML template, we need to make a v-on:click on the “New” button:</p><pre>&lt;button class=&quot;toolbar-button&quot; v-on:click=&quot;createNote()&quot;&gt;<br>  New<br>&lt;/button&gt;</pre><p>And in the JavaScript, we need to define the createNote method:</p><pre>  methods: {<br>    selectNote: function(note) {<br>      this.selectedNote = note;<br>    },<br>    createNote: function() {<br>      var newNote = {<br>        id: Date.now(),<br>        body: &quot;&quot;,<br>        timestamp: Date.now()<br>      };<br>      this.notes.push(newNote);<br>      this.selectedNote = newNote;<br>    }<br>  },</pre><p>(Here I’m using Date.now() as a quick and dirty way of generating a id, it should be replaced with a more robust approach for a guaranteed unique id.) Creating a new note is as simple as updating the notes and selectedNote data — Vue.js handles all the DOM updates necessary!</p><h3>Delete the selected note with a button</h3><p>Again, it’s pretty easy to get the delete feature to work. In the HTML template, we need to make a v-on:click on the “Delete” button:</p><pre>&lt;button class=&quot;toolbar-button&quot; v-on:click=&quot;deleteNote()&quot;&gt;<br>  Delete<br>&lt;/button&gt;</pre><p>And in the JavaScript, we need to define the deleteNote method:</p><pre>  methods: {<br>    selectNote: function(note) {<br>      this.selectedNote = note;<br>    },<br>    createNote: function() {<br>      var newNote = {<br>        id: Date.now(),<br>        body: &quot;&quot;,<br>        timestamp: Date.now()<br>      };<br>      this.notes.push(newNote);<br>      this.selectedNote = newNote;<br>    },<br>    deleteNote: function(note) {<br>      var index = this.notes.indexOf(this.selectedNote);<br>      if (index !== -1) {<br>        this.notes.splice(index, 1);<br>        if (this.transformedNotes.length &gt; 0) {<br>          this.selectedNote = this.transformedNotes[0];<br>        } else {<br>          this.selectedNote = {};<br>        }<br>      }<br>    }<br>  },</pre><p>Note that when you delete the selected note, you have to determine which note to select in its place. Finally, we need to make sure the editor itself is hidden when there are no notes. This can be done with one easy change using the v-if directive in the HTML template:</p><pre>&lt;div class=&quot;note-editor&quot; v-if=&quot;selectedNote.id&quot;&gt;<br>  &lt;p class=&quot;note-editor-info&quot;&gt;<br>    {{ selectedNote.timestamp | formatTimestamp }}<br>  &lt;/p&gt;<br>  &lt;textarea class=&quot;note-editor-input&quot; v-model=&quot;selectedNote.body&quot;&gt;<br>  &lt;/textarea&gt;<br>&lt;/div&gt;</pre><h3>Filter notes on search input</h3><p>The final feature I’m going to implement is to be able to search notes immediately as you type in the search input. The first step is to add a v-model in the HTML template on the search input:</p><pre>&lt;input class=&quot;toolbar-search&quot; type=&quot;text&quot; placeholder=&quot;Search...&quot; v-model=&quot;searchNoteText&quot;&gt;</pre><p>Don’t forget to initialize the searchNoteText data:</p><pre>  data: {<br>    notes: [],<br>    selectedNote: {},<br>    searchNoteText: &quot;&quot;<br>  },</pre><p>Now in order to get the filtering effect, we simply need to change the already existing computed property transformedNotes to both filter and sort (instead of just sort):</p><pre>  computed: {<br>    transformedNotes: function() {<br>      return this.notes<br>        .filter(function(note) {<br>          return note.body.toLowerCase().indexOf(this.searchNoteText.toLowerCase()) !== -1;<br>        }.bind(this))<br>        .sort(function(a, b) {<br>          return b.timestamp - a.timestamp;<br>        });<br>    }<br>  },</pre><p>Perfect! The only thing missing is that filtering the list should also select the appropriate note, which means the selectedNote data should update whenever the searchNoteText data changes. We can use a watch function on searchNoteText for that:</p><pre>  watch: {<br>    notes: {<br>      handler: function() {<br>        this.selectedNote.timestamp = Date.now();<br>      },<br>      deep: true<br>    },<br>    searchNoteText: function() {<br>      if (this.transformedNotes.length === 0) {<br>        this.selectedNote = {};<br>      } else if (this.transformedNotes.indexOf(this.selectedNote) === -1) {<br>        this.selectedNote = this.transformedNotes[0];<br>      }<br>    }<br>  }</pre><p>Note that I’m referring to this.transformedNotes in the function, since I’m trying to select the first note in the list of filtered and sorted notes.</p><p>Here is the final working example in all its glory:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Fkoza3tax%2Fembedded%2F&amp;url=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Fkoza3tax%2F&amp;image=https%3A%2F%2Fwww.gravatar.com%2Favatar%2Fbe58ab79b144e42a4cc9300d5206c06d%2F%3Fdefault%3D%26s%3D80&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=jsfiddle" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/2b44641de90ca6d727038aecbb1fb7b5/href">https://medium.com/media/2b44641de90ca6d727038aecbb1fb7b5/href</a></iframe><h3>Conclusion</h3><p>If you compare working with Vue.js to <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">jQuery</a>, you can see that the things you’re concerned about as a programmer are very different! In some sense I would consider jQuery to be easier to get started with, particularly if you have a good understanding of the DOM. But as we got deeper into the features, it was a lot simpler to write the Vue.js code, since we can keep our focus on data manipulation as opposed to both data manipulation plus DOM manipulation.</p><p>Learning Vue.js might seem a bit daunting to an absolute beginner — we covered the v-for, v-on, v-bind, v-model, and v-if HTML template directives, as well as the data, mounted, computed, filters, methods, and watch Vue instance options. However, getting started with Vue.js is a lot easier than this — you can get pretty far with with just the v-for, v-model, and v-click directives with the data and methods Vue instance options! Basically you should learn new directives and options as the need arises — this is what Vue.js means when it says it’s a progressive framework. And compared to the other frameworks we’ll be looking at in this series, it’s definitely the easiest to get up and running!</p><p>So the promise of Vue.js (or any modern JavaScript framework really) is that it’s a bit harder to get started, but once you learn the framework it becomes simpler to add new features. In the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">next part</a> of this series, we’ll take that concept to the next step by rebuilding the same app using Vue.js, this time with components. Stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8b8614e4f324" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">Comparing Frontend Approaches Part 3: Vue.js</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Comparing Frontend Approaches Part 2: jQuery]]></title>
            <link>https://medium.com/actualize-network/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/d92abe80ef25</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[jquery]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Sat, 13 Jan 2018 23:13:42 GMT</pubDate>
            <atom:updated>2018-01-16T05:57:33.423Z</atom:updated>
            <content:encoded><![CDATA[<h4>A look at jQuery, Vue.js, React, and Elm</h4><ul><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">Part 1: Introduction</a></li><li><strong>Part 2: jQuery</strong></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">Part 3: Vue.js</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Part 4: Vue.js with components</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-5-react-b51fd7d075fe">Part 5: React</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-6-elm-578714526164">Part 6: Elm</a></li><li><a href="https://medium.com/@peterxjang/comparing-frontend-approaches-part-7-final-thoughts-69cdba516f86">Part 7: Final thoughts</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/736/1*7aM1iGhORpH_edtcYbtQsA.png" /></figure><p>In this part we will be implementing the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">web based clone</a> of the <a href="https://support.apple.com/kb/PH22608?locale=en_US">Mac Notes app</a> using <a href="https://jquery.com/">jQuery</a>. jQuery is a JavaScript library that came out in 2006, and within several years became the most popular JavaScript library in use on the web (used by <a href="http://trends.builtwith.com/javascript/jQuery">70% of the top 100,000</a> sites online).</p><p>jQuery provides an intuitive API to directly manipulate the DOM to create interactive websites. In 2017, jQuery’s DOM manipulation approach is far less dominant, with templating and virtual DOM based approaches taking over. Nevertheless, jQuery remains an excellent library that’s reasonably lightweight, easy to get started with, and still used by many apps in production today. Let’s see what it’s like to use in practice!</p><blockquote>Note — one could argue that it would make more sense to start with vanilla JavaScript instead of jQuery, which is completely reasonable. jQuery provides some concise helper functions that makes it easier to demonstrate certain features, but you should feel free to mentally substitute any jQuery specific approaches I’m taking with the corresponding vanilla JavaScript — the general approach should be the same.</blockquote><h3>Installation</h3><p>Starting from our <a href="https://jsfiddle.net/peterxjang/vtsjc5w9">initial template</a> in <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">part 1</a>, installing jQuery requires only 2 lines in the HTML head tag:</p><pre>&lt;script src=&quot;https://code.jquery.com/jquery-3.2.1.js&quot;<br>  integrity=&quot;sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE=&quot;<br>  crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;</pre><pre>&lt;script src=&quot;js/notes.js&quot; defer&gt;&lt;/script&gt;</pre><p>The first line includes the jQuery library from a CDN (the integrity and crossorigin attributes are for <a href="https://www.w3.org/TR/SRI/">security</a> when using outside resources). The second line includes the app’s JavaScript and only has one change from the original template — the defer attribute. This ensures that the notes.js file is loaded only after the DOM is ready (before this attribute existed, you had to resort to tactics such as putting the script tag at the end of the body tag or using a DOMContentLoaded event listener wrapping your entire code).</p><h3>Display note titles from an array of notes</h3><p>Now that jQuery’s installed, let’s change the starting template to generate the dynamic notes list using JavaScript.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Fw2xc3kkm%2Fembedded%2F&amp;url=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Fw2xc3kkm%2F&amp;image=https%3A%2F%2Fwww.gravatar.com%2Favatar%2Fbe58ab79b144e42a4cc9300d5206c06d%2F%3Fdefault%3D%26s%3D80&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=jsfiddle" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/ecda8e526659f72a35d23bd747eefc56/href">https://medium.com/media/ecda8e526659f72a35d23bd747eefc56/href</a></iframe><p>Here I’m representing the notes as an array of objects, where each object has an id, a body, and a timestamp. The Date.now() returns the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC.</p><pre>var notes = [<br>  {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>  {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>  {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>];</pre><p>Once we have the notes data, we can delete the placeholder HTML content and generate the note selectors ourselves:</p><pre>notes.forEach(function(note) {<br>  $(&#39;.note-selectors&#39;).append(<br>    &#39;&lt;div class=&quot;note-selector&quot;&gt;&#39; +<br>      &#39;&lt;p class=&quot;note-selector-title&quot;&gt;&#39; + note.body + &#39;&lt;/p&gt;&#39; +<br>      &#39;&lt;p class=&quot;note-selector-timestamp&quot;&gt;&#39; + note.timestamp + &#39;&lt;/p&gt;&#39; +<br>    &#39;&lt;/div&gt;&#39;<br>  );<br>});</pre><p>Here I’m using jQuery to both create new DOM elements and append them directly into the DOM. Right now I’m temporarily using the body and integer timestamp directly as the title and timestamp — they’ll need to be formatted appropriately to look pretty.</p><blockquote>Note that there is an additional concern that’s more subtle — adding the note.body directly into the DOM here can open up an <a href="https://www.acunetix.com/websitesecurity/cross-site-scripting/">XSS vulnerability</a>, where a malicious user can enter a &lt;script&gt; tag in the HTML input and run unwanted JavaScript. You would need to sanitize the input yourself (oddly enough jQuery’s .text() method <a href="https://watchitlater.com/blog/2011/10/cross-site-scripting-vulnerability-with-javascript-and-jquery/">won’t be enough here</a>), which is one disadvantage of using a library over a framework (which generally have such security features built in).</blockquote><h3>Use methods to sort and format notes</h3><p>Now let’s make helper methods to make sure the notes are sorted (newest first) and formatted properly (titles should be computed from the body, timestamps should be converted from milliseconds into a human readable string).</p><pre>function transformNotes(notes) {<br>  return notes.slice().sort(function(a, b) {<br>    return b.timestamp - a.timestamp;<br>  });<br>}</pre><pre>function formatTitle(body) {<br>  var maxLength = 20;<br>  if (body.length &gt; maxLength) {<br>    return body.substring(0, maxLength - 3) + &#39;...&#39;;<br>  } else if (body.length === 0) {<br>    return &quot;New note&quot;;<br>  } else {<br>    return body;<br>  }<br>}</pre><pre>function formatTimestamp(timestamp) {<br>  return new Date(timestamp).toUTCString();<br>}</pre><p>The logic here is somewhat simplified — the title should account for line breaks, the string output from the native toUTCString isn’t the prettiest, etc. However, since they are separate pure functions, it will be simple to make them more robust later. The loop would be modified to use the functions as follows:</p><pre>transformNotes(notes).forEach(function(note) {<br>  $(&#39;.note-selectors&#39;).append(<br>    &#39;&lt;div class=&quot;note-selector&quot;&gt;&#39; +<br>      &#39;&lt;p class=&quot;note-selector-title&quot;&gt;&#39; + formatTitle(note.body) + &#39;&lt;/p&gt;&#39; +<br>      &#39;&lt;p class=&quot;note-selector-timestamp&quot;&gt;&#39; + formatTimestamp(note.timestamp) + &#39;&lt;/p&gt;&#39; +<br>    &#39;&lt;/div&gt;&#39;<br>  );<br>});</pre><h3>Select a note on title click</h3><p>Now let’s implement the ability to actually select notes. Clicking on a note title should both highlight the selected note on the left as well as display the contents in the editor on the right. Here’s a complete working example, which we’ll go over in detail:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Fnkpg036t%2Fembedded%2F&amp;url=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Fnkpg036t%2F&amp;image=https%3A%2F%2Fwww.gravatar.com%2Favatar%2Fbe58ab79b144e42a4cc9300d5206c06d%2F%3Fdefault%3D%26s%3D80&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=jsfiddle" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/1f200dd83fc5ac22426bffe6a0f62cc2/href">https://medium.com/media/1f200dd83fc5ac22426bffe6a0f62cc2/href</a></iframe><p>First we need to keep track of a new variable selectedNote (which we’ll default to the first note).</p><pre>var notes = [<br>  {id: 1, body: &quot;This is a first test&quot;, timestamp: Date.now()},<br>  {id: 2, body: &quot;This is a second test&quot;, timestamp: Date.now()},<br>  {id: 3, body: &quot;This is a third test&quot;, timestamp: Date.now()}<br>];<br>var selectedNote = notes[0];</pre><p>Now comes the tricky part — in order to be able to click on a note title and display its corresponding information, we need to create a binding between the DOM and the data in JavaScript. One way to pull this off is by embedding the relevant data into the DOM using <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes">HTML data-* attributes</a>. In this case, each time I’m creating a new note selector DOM element with jQuery, I want to add the relevant note data into the DOM element itself using data-id, data-body, and data-timestamp attributes. For the sake of code organization, I extracted the code that creates the note selector DOM elements to a function called domCreateNoteSelectors:</p><pre>function domCreateNoteSelectors(notes, selectedNote) {<br>  transformNotes(notes).forEach(function(note) {<br>    var $noteSelector = $(<br>      &#39;&lt;div class=&quot;note-selector&#39; + (note === selectedNote ? &#39; active&#39; : &#39;&#39;) + &#39;&quot;&gt;&#39; +<br>        &#39;&lt;p class=&quot;note-selector-title&quot;&gt;&#39; + formatTitle(note.body) + &#39;&lt;/p&gt;&#39; +<br>        &#39;&lt;p class=&quot;note-selector-timestamp&quot;&gt;&#39; + formatTimestamp(note.timestamp) + &#39;&lt;/p&gt;&#39; +<br>      &#39;&lt;/div&gt;&#39;<br>    );<br>    $noteSelector.data(note);<br>    $(&#39;.note-selectors&#39;).append($noteSelector);<br>  });<br>}</pre><p>Note the variable name $noteSelector is using the convention of putting a $ in front of any variable which represents a DOM element. The key line which binds the data in JavaScript to the DOM is:</p><pre>    $noteSelector.data(note);</pre><p>This handy jQuery method takes all the keys in the note object (in this case id, body, and timestamp) and creates corresponding data-* attributes in the DOM element (in this case data-id, data-body, and data-timestamp).</p><p>Now that we have data binding, we’ll need to make an event listener to trigger whenever the user clicks on a note title:</p><pre>$(&#39;.note-selectors&#39;).on(&#39;click&#39;, &#39;.note-selector&#39;, function() {<br>  $(&#39;.note-selector&#39;).removeClass(&#39;active&#39;);<br>  $(this).addClass(&#39;active&#39;);<br>  domUpdateNoteEditor($(this).data());<br>});</pre><p>(Note that I’m putting a delegated event listener on the .note-selectors DOM element and not on the .note-selector directly— this is because the .note-selector elements don’t exist until later!) The event listener has two responsibilities — one is to update the style such that the selector appears highlighted, which is achieved by removing the .active class from all note selectors, then adding it back to the note selector that was just clicked. The other responsibility is to update the note editor on the right to display the appropriate note data. The note data is extracted from the DOM using $(this).data(), and is passed to a separate domUpdateNoteEditor function:</p><pre>function domUpdateNoteEditor(selectedNote) {<br>  $(&#39;.note-editor-info&#39;)<br>    .html(formatTimestamp(selectedNote.timestamp));<br>  $(&#39;.note-editor-input&#39;)<br>    .val(selectedNote.body);<br>}</pre><h3>Edit the selected note on editor input</h3><p>Next we’ll bind the &lt;textarea&gt; to the note data. Each time the user changes the text, there’s quite a few things that need to be updated:</p><ul><li>The underlying note data (body and timestamp)</li><li>The displayed timestamp in the note editor</li><li>The displayed title and timestamp in the current note selector</li><li>The sort order of the note selectors (the current one should be on top)</li></ul><p>Here’s the code to make all these changes happen:</p><pre>$(&#39;.note-editor-input&#39;).on(&#39;input propertychange&#39;, function(event) {<br>  // Update the note data<br>  var body = $(this).val();<br>  var timestamp = Date.now();<br>  $(&#39;.note-selector.active&#39;).data(&#39;body&#39;, body);<br>  $(&#39;.note-selector.active&#39;).data(&#39;timestamp&#39;, timestamp);</pre><pre>  // Update the DOM note editor timestamp<br>  $(&#39;.note-editor-info&#39;).html(formatTimestamp(timestamp));</pre><pre>  // Update the DOM currently selected note selector<br>  $(&#39;.note-selector.active .note-selector-title&#39;)<br>    .html(formatTitle(body));<br>  $(&#39;.note-selector.active .note-selector-timestamp&#39;)<br>    .html(formatTimestamp(timestamp));</pre><pre>  // Update the DOM note selectors sorting order<br>  var $active = $(&#39;.note-selector.active&#39;).detach();<br>  $(&#39;.note-selectors&#39;).prepend($active);<br>});</pre><p>This is where we start to see some difficulties with jQuery — while the DOM manipulation code itself is fairly straightforward, it becomes more and more complicated to remember the different parts of the DOM that need to be updated with each change.</p><h3>Create a new note with a button</h3><p>Now let’s implement the ability to create a new note. Clicking on the “New” button should create a new note (new id, no body, current timestamp). The new note should become the currently selected note and appear at the top of the list of note selectors. Here’s a complete working example, which we’ll go over in detail:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Frnemvjct%2Fembedded%2F&amp;url=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Frnemvjct%2F&amp;image=https%3A%2F%2Fwww.gravatar.com%2Favatar%2Fbe58ab79b144e42a4cc9300d5206c06d%2F%3Fdefault%3D%26s%3D80&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=jsfiddle" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/4fd0c0ab59a51db2965541c001f785ee/href">https://medium.com/media/4fd0c0ab59a51db2965541c001f785ee/href</a></iframe><p>Originally we made the domCreateNoteSelectors function to create note selectors in a loop. We’ll need to extract the code to make a single note selector into a separate domCreateNoteSelector function (so we can reuse it with the new note button):</p><pre>function domCreateNoteSelector(note, selectedNote) {<br>  var $noteSelector = $(<br>    &#39;&lt;div class=&quot;note-selector&#39; + (note === selectedNote ? &#39; active&#39; : &#39;&#39;) + &#39;&quot;&gt;&#39; +<br>      &#39;&lt;p class=&quot;note-selector-title&quot;&gt;&#39; + formatTitle(note.body) + &#39;&lt;/p&gt;&#39; +<br>      &#39;&lt;p class=&quot;note-selector-timestamp&quot;&gt;&#39; + formatTimestamp(note.timestamp) + &#39;&lt;/p&gt;&#39; +<br>    &#39;&lt;/div&gt;&#39;<br>  );<br>  $noteSelector.data(note);<br>  return $noteSelector;<br>}</pre><pre>// ...</pre><pre>function domCreateNoteSelectors(notes, selectedNote) {<br>  transformNotes(notes).forEach(function(note) {<br>    var $noteSelector = domCreateNoteSelector(note, selectedNote);<br>    $noteSelector.data(note);<br>    $(&#39;.note-selectors&#39;).append($noteSelector);<br>  });<br>}</pre><p>Here’s the code that runs when the user clicks the “New” button (note that I added the .toolbar-button-new class in the HTML to make it easier to target the new button element):</p><pre>$(&#39;.toolbar-button-new&#39;).on(&#39;click&#39;, function() {<br>  $(&#39;.note-selector&#39;).removeClass(&#39;active&#39;);<br>  var note = {<br>    id: Date.now(),<br>    body: &quot;&quot;,<br>    timestamp: Date.now()<br>  };<br>  var $noteSelector = domCreateNoteSelector(note, note);<br>  $(&#39;.note-selectors&#39;).prepend($noteSelector);<br>  domUpdateNoteEditor(note);<br>});</pre><p>Here I’m using Date.now() as a quick and dirty way of generating a id, it should be replaced with a more robust approach for a guaranteed unique id. In order to make sure the new note is highlighted, I first removed the .active class from all note selectors, then created the new note selector with the domCreateNoteSelector function, which takes in the note to create along with the currently selected note (which in this case is itself). This works fine, but again it’s getting a little harder to follow exactly how the DOM is being updated.</p><h3>Delete the selected note with a button</h3><p>The delete button is in theory easy enough to implement — add an event listener to the “Delete” button which removes the currently selected note selector (which is marked with the class .active) from the DOM.</p><pre>$(&#39;.toolbar-button-delete&#39;).on(&#39;click&#39;, function() {<br>  $(&#39;.note-selector.active&#39;).remove();<br>});</pre><p>Unfortunately, this doesn’t address what note should be marked as the currently selected note after the current one is deleted. Here’s a second attempt:</p><pre>$(&#39;.toolbar-button-delete&#39;).on(&#39;click&#39;, function() {<br>  $(&#39;.note-selector.active&#39;).remove();<br>  var children = $(&#39;.note-selectors&#39;).children();<br>  var $noteSelector = $(children[0]);<br>  $noteSelector.addClass(&#39;active&#39;);<br>  domUpdateNoteEditor($noteSelector.data());<br>});</pre><p>This code will grab all the note selectors from the DOM and select the first one. Unfortunately, this doesn’t address what should happen when you delete the LAST note. Let’s say we want the app to hide the note editor completely if there are no notes:</p><pre>$(&#39;.toolbar-button-delete&#39;).on(&#39;click&#39;, function() {<br>  $(&#39;.note-selector.active&#39;).remove();<br>  var children = $(&#39;.note-selectors&#39;).children();<br>  if (children.length &gt; 0) {<br>    var $noteSelector = $(children[0]);<br>    $noteSelector.addClass(&#39;active&#39;);<br>    domUpdateNoteEditor($noteSelector.data());<br>  } else {<br>    $(&#39;.note-editor&#39;).hide();<br>  }<br>});</pre><p>This works, but once the note editor is hidden, it will never unhide! So now we have to make sure the note editor is shown when you create a new note:</p><pre>$(&#39;.toolbar-button-new&#39;).on(&#39;click&#39;, function() {<br>  $(&#39;.note-editor&#39;).show();<br>  //...<br>});</pre><p>This completely works now, but we’re getting into the territory of what is known as “jQuery spaghetti” — the delete note feature requires writing code in both the delete button event listener as well as the new button event listener, which is highly unintuitive. As more features get added to the app, these types of unrelated dependencies start cropping up in more and more places, making it harder to reason about the code.</p><h3>Filter notes on search input</h3><p>The final feature I’m going to implement is to be able to search notes immediately as you type in the search input. Before we implement it, I’m first going to extract out the code from the delete event listener that selects the appropriate note when the list changes into a function called domSelectDefaultChild, since we’ll need similar functionality when dynamically searching.</p><pre>function domSelectDefaultChild() {<br>  var children = $(&#39;.note-selector:visible&#39;);<br>  if (children.length &gt; 0) {<br>    var $noteSelector = $(children[0]);<br>    if ($(&#39;.note-selector.active&#39;).length === 0) {<br>      $noteSelector.addClass(&#39;active&#39;);<br>      domUpdateNoteEditor($noteSelector.data());<br>    }<br>  } else {<br>    $(&#39;.note-editor&#39;).hide();<br>  }<br>}</pre><pre>// ...</pre><pre>$(&#39;.toolbar-button-delete&#39;).on(&#39;click&#39;, function() {<br>  $(&#39;.note-selector.active&#39;).remove();<br>  domSelectDefaultChild();<br>});</pre><p>Now let’s add an event listener on the search input. This code will need to do the following things:</p><ul><li>Show the note editor in case it’s hidden</li><li>Get the search text the user entered</li><li>Loop through each note selector and show or hide it based on whether or not the note’s body (in the DOM data-body attribute) matches the user’s search text</li><li>Select a new note if necessary</li></ul><p>The code to accomplish this is as follows:</p><pre>$(&#39;.toolbar-search&#39;).on(&#39;input propertychange&#39;, function() {<br>  $(&#39;.note-editor&#39;).show();<br>  var searchNoteText = $(this).val();<br>  $(&#39;.note-selector&#39;).each(function() {<br>    var $note = $(this);<br>    if ($note.data().body.toLowerCase().indexOf(searchNoteText.toLowerCase()) === -1) {<br>      $note.hide();<br>      if ($note.hasClass(&#39;active&#39;)) {<br>        $note.removeClass(&#39;active&#39;);<br>      }<br>    } else {<br>      $note.show();<br>    }<br>  });<br>  domSelectDefaultChild();<br>});</pre><p>Here is the final working example in all its glory:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Fa0qj4njz%2Fembedded%2F&amp;url=https%3A%2F%2Fjsfiddle.net%2Fpeterxjang%2Fa0qj4njz%2F&amp;image=https%3A%2F%2Fwww.gravatar.com%2Favatar%2Fbe58ab79b144e42a4cc9300d5206c06d%2F%3Fdefault%3D%26s%3D80&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=jsfiddle" width="600" height="400" frameborder="0" scrolling="no"><a href="https://medium.com/media/c71dce90b49d8d0a52060987b7289bb1/href">https://medium.com/media/c71dce90b49d8d0a52060987b7289bb1/href</a></iframe><h3>Conclusion</h3><p>I’ve noted in a couple of places where the jQuery code is starting to get hard to manage. Still, I would consider this app to be reasonably simple, so jQuery works well enough here, especially if you’re diligent about extracting reusable functions. There’s a certain beauty to jQuery where the code closely matches what you’re trying to accomplish in terms of DOM manipulation. However, it does get harder and harder to keep track of how the code is working as you add more features.</p><p>The approaches I’ve taken here certainly don’t represent the best or only way to implement these features. You may be able to better organize the code using advanced features like <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver">mutation observers</a>, modules, etc. jQuery is unopinionated and allows you to come up with your own patterns to organize your code. But at that point, you may want to look at frameworks which have already established a set of patterns to make it easier to create new apps. In the <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">next part</a> of this series, we’ll take a look at building the same set of features from scratch using the <a href="https://vuejs.org/">Vue.js</a> framework. Stay tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d92abe80ef25" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/comparing-frontend-frameworks-part-2-jquery-d92abe80ef25">Comparing Frontend Approaches Part 2: jQuery</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Prototyping with Vue.js and Bootstrap]]></title>
            <link>https://medium.com/actualize-network/prototyping-with-vue-js-and-bootstrap-2404efe93d6?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/2404efe93d6</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[bootstrap]]></category>
            <category><![CDATA[vuejs]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Sat, 13 Jan 2018 22:33:16 GMT</pubDate>
            <atom:updated>2017-12-29T06:17:06.594Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UTO3Xix1EbdZXYP4Bam_eg.png" /></figure><p>Let’s say you’re in a situation where you want to create a quick prototype frontend for a web application, something you would throw together in a couple of hours. In this scenario it might be overkill to use a <a href="https://medium.com/the-node-js-collection/modern-javascript-explained-for-dinosaurs-f695e9747b70">modern build process</a> with a component-based frontend architecture. One good combo can be to use <a href="https://getbootstrap.com/">Bootstrap</a> for visuals and <a href="https://vuejs.org/">Vue.js</a> for interactivity. These frameworks work well for creating prototypes, because they don’t necessarily require any tooling or configuration — you can start with this <a href="https://getbootstrap.com/docs/4.0/getting-started/introduction/#starter-template">Bootstrap HTML template</a>, add the <a href="https://vuejs.org/v2/guide/#Getting-Started">script tag for Vue.js</a>, and you’re good to go!</p><blockquote><strong>Note</strong>: My guess is that there will be some visceral responses here about how everyone should <strong>always</strong> use npm or yarn to manage dependencies and that no one should <strong>ever</strong> include individual script tags in their HTML. If that’s your reaction, this article probably isn’t for you — if you feel like you can rapidly prototype while still using your build tools of choice, go for it! This article is for people who want to avoid the overhead of tooling and configuration and still be able to leverage framework features painlessly for prototyping.</blockquote><p>Here’s a quick example of rendering a list of five items using some basic Bootstrap classes for styling:</p><pre>&lt;div class=&quot;container&quot;&gt;<br>  &lt;h1&gt;Items&lt;/h1&gt;<br>  &lt;div class=&quot;list-group&quot;&gt;<br>    &lt;button type=&quot;button&quot; class=&quot;list-group-item list-group-item-action&quot;&gt;Morbi&lt;/button&gt;<br>    &lt;button type=&quot;button&quot; class=&quot;list-group-item list-group-item-action&quot;&gt;Fusce&lt;/button&gt;<br>    &lt;button type=&quot;button&quot; class=&quot;list-group-item list-group-item-action&quot;&gt;Aenean&lt;/button&gt;<br>    &lt;button type=&quot;button&quot; class=&quot;list-group-item list-group-item-action&quot;&gt;Rhoncus&lt;/button&gt;<br>    &lt;button type=&quot;button&quot; class=&quot;list-group-item list-group-item-action&quot;&gt;Sed&lt;/button&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HFqBoMzEIKl-pZvAGsMHRw.png" /></figure><p>Not too exciting yet (we’ll get there). Now let’s add some Vue.js to render the same list of items dynamically with a loop:</p><pre>&lt;div <strong>id=&quot;app&quot;</strong> class=&quot;container&quot;&gt;<br>  &lt;h1&gt;Items&lt;/h1&gt;<br>  &lt;div class=&quot;list-group&quot;&gt;<br>    &lt;button <br>      type=&quot;button&quot;<br>      class=&quot;list-group-item list-group-item-action&quot;<br>      <strong>v-for=&quot;item in items&quot;</strong><br>    &gt;<br>      <strong>{{ item.title }}</strong><br>    &lt;/button&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><p>Here we’re using the Vue.js v-for directive to render a list of items, which are defined in the Vue instance in JavaScript:</p><pre>var app = new Vue({<br>  el: &quot;#app&quot;,<br>  data: {<br>    items: [<br>      {title: &quot;Morbi&quot;, body: &quot;Morbi nec dictum...&quot;},<br>      {title: &quot;Fusce&quot;, body: &quot;Fusce molestie...&quot;},<br>      {title: &quot;Aenean&quot;, body: &quot;Aenean et lectus...&quot;},<br>      {title: &quot;Rhoncus&quot;, body: &quot;Rhoncus eleifend...&quot;},<br>      {title: &quot;Sed&quot;, body: &quot;Sed tellus...&quot;}<br>    ]<br>  }<br>})</pre><p>You can see a working example with the full HTML and JavaScript <a href="https://codepen.io/peterxjang/pen/jYBKmN?editors=1010">here</a>.</p><p>This may not seem that impressive style-wise, but now we have all of Bootstrap’s visual features at our fingertips! Let’s say we want to be able to click on each item to show a <a href="https://getbootstrap.com/docs/4.0/components/modal/#live-demo">Bootstrap modal</a> with more details about the item. A Bootstrap modal is a &lt;div&gt; that is hidden until something triggers it, usually a button on the page. Here’s the example from Bootstrap’s documentation of a button that activates a modal:</p><pre>&lt;!-- Button trigger modal --&gt;<br>&lt;button type=&quot;button&quot; class=&quot;btn btn-primary&quot; data-toggle=&quot;modal&quot; data-target=&quot;#exampleModal&quot;&gt;<br>  Launch demo modal<br>&lt;/button&gt;<br><br>&lt;!-- Modal --&gt;<br>&lt;div class=&quot;modal fade&quot; id=&quot;exampleModal&quot; tabindex=&quot;-1&quot; role=&quot;dialog&quot; aria-labelledby=&quot;exampleModalLabel&quot; aria-hidden=&quot;true&quot;&gt;<br>  &lt;div class=&quot;modal-dialog&quot; role=&quot;document&quot;&gt;<br>    &lt;div class=&quot;modal-content&quot;&gt;<br>      &lt;div class=&quot;modal-header&quot;&gt;<br>        &lt;h5 class=&quot;modal-title&quot; id=&quot;exampleModalLabel&quot;&gt;Modal title&lt;/h5&gt;<br>        &lt;button type=&quot;button&quot; class=&quot;close&quot; data-dismiss=&quot;modal&quot; aria-label=&quot;Close&quot;&gt;<br>          &lt;span aria-hidden=&quot;true&quot;&gt;&amp;times;&lt;/span&gt;<br>        &lt;/button&gt;<br>      &lt;/div&gt;<br>      &lt;div class=&quot;modal-body&quot;&gt;<br>        ...<br>      &lt;/div&gt;<br>      &lt;div class=&quot;modal-footer&quot;&gt;<br>        &lt;button type=&quot;button&quot; class=&quot;btn btn-secondary&quot; data-dismiss=&quot;modal&quot;&gt;Close&lt;/button&gt;<br>        &lt;button type=&quot;button&quot; class=&quot;btn btn-primary&quot;&gt;Save changes&lt;/button&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><p>What is not immediately apparent is that Bootstrap is toggling the &lt;div&gt; using JavaScript (which was loaded elsewhere as a script in the HTML). The user doesn’t need to write any JavaScript for this example to work — the only thing necessary is the data-toggle=&quot;modal&quot; and data-target=&quot;#exampleModal&quot; attributes in the button that corresponds with the id=&quot;exampleModal&quot; in the modal div.</p><p>In order to use this modal with Vue.js, we have to make sure Bootstrap’s JavaScript doesn’t conflict with Vue’s JavaScript. There are 3 different approaches that come to mind:</p><ul><li><strong>Make a Vue.js component for the modal</strong>. This would require passing in props for each individual item, as well as toggling the modal using Vue’s events (instead of Bootstrap’s data-toggle and data-target attributes). The rough architecture would look like <a href="https://vuejs.org/v2/examples/modal.html">this example</a>. This is a clean approach, but it seems over-architected for the purposes of making a quick prototype.</li><li><strong>Make multiple Bootstrap modals within the loop</strong>. This would require using Bootstrap’sdata-toggle and data-target attributes in conjunction with using Vue.js to bind a unique id attribute for each modal (otherwise each item would toggle the same modal). This approach works, but it seems wasteful to make multiple modals when you only need one with dynamic content.</li><li><strong>Make one Bootstrap modal and keep track of the current item</strong>. This would require using Bootstrap’sdata-toggle and data-target attributes in conjunction with a Vue event to update the current item when an item is clicked. This seems like the best approach for a prototype, since it allows us to use a single dynamic modal and not have to rewrite Bootstrap’s JavaScript to toggle it.</li></ul><p>There is actually a fourth approach, which is to use a library like <a href="https://bootstrap-vue.js.org/">Bootstrap-Vue</a>, which is essentially code written by someone using the first approach to wrap all of Bootstrap’s components using Vue.js components. Again, a potentially useful approach, but it seems like overkill for just one modal.</p><p>Here’s what the code would look like for the third approach — make a single Bootstrap modal and keep track of the current item that was clicked. There’s surprisingly little code to add other than the HTML for the modal itself. The HTML changes as follows:</p><pre>&lt;div id=&quot;app&quot; class=&quot;container&quot;&gt;<br>  &lt;h1&gt;Items&lt;/h1&gt;<br>  &lt;div class=&quot;list-group&quot;&gt;<br>    &lt;button<br>      type=&quot;button&quot;<br>      class=&quot;list-group-item list-group-item-action&quot;<br>      <strong>data-toggle=&quot;modal&quot;</strong><br>      <strong>data-target=&quot;#exampleModal&quot;</strong><br>      v-for=&quot;item in items&quot;<br>      <strong>v-on:click=&quot;setCurrentItem(item)&quot;</strong><br>    &gt;<br>      {{ item.title }}<br>    &lt;/button&gt;<br>  &lt;/div&gt;</pre><pre>  <strong>&lt;!-- Modal --&gt;<br>  &lt;div class=&quot;modal fade&quot; id=&quot;exampleModal&quot; tabindex=&quot;-1&quot; role=&quot;dialog&quot; aria-labelledby=&quot;exampleModalLabel&quot; aria-hidden=&quot;true&quot;&gt;<br>    &lt;div class=&quot;modal-dialog&quot; role=&quot;document&quot;&gt;<br>      &lt;div class=&quot;modal-content&quot;&gt;<br>        &lt;div class=&quot;modal-header&quot;&gt;<br>          &lt;h5 class=&quot;modal-title&quot; id=&quot;exampleModalLabel&quot;&gt;<br>            {{ currentItem.title }}<br>          &lt;/h5&gt;<br>          &lt;button type=&quot;button&quot; class=&quot;close&quot; data-dismiss=&quot;modal&quot; aria-label=&quot;Close&quot;&gt;<br>            &lt;span aria-hidden=&quot;true&quot;&gt;&amp;times;&lt;/span&gt;<br>          &lt;/button&gt;<br>        &lt;/div&gt;<br>        &lt;div class=&quot;modal-body&quot;&gt;<br>          {{ currentItem.body }}<br>        &lt;/div&gt;<br>        &lt;div class=&quot;modal-footer&quot;&gt;<br>          &lt;button type=&quot;button&quot; class=&quot;btn btn-secondary&quot; data-dismiss=&quot;modal&quot;&gt;Close&lt;/button&gt;<br>          &lt;button type=&quot;button&quot; class=&quot;btn btn-primary&quot;&gt;Save changes&lt;/button&gt;<br>        &lt;/div&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>  &lt;/div&gt;  </strong>    <br>    <br>&lt;/div&gt;</pre><p>Here we’re using Bootstrap’sdata-toggle and data-target attributes, which means we can rely on Bootstrap’s JavaScript to show and hide the modal without any more effort. We’re also using Vue’s v-on:click to set the current item equal to whichever one was clicked. The modal itself displays information about the current item. The corresponding JavaScript looks like:</p><pre>var app = new Vue({<br>  el: &quot;#app&quot;,<br>  data: {<br>    items: [<br>      {title: &quot;Morbi&quot;, body: &quot;Morbi nec dictum...&quot;},<br>      {title: &quot;Fusce&quot;, body: &quot;Fusce molestie...&quot;},<br>      {title: &quot;Aenean&quot;, body: &quot;Aenean et lectus...&quot;},<br>      {title: &quot;Rhoncus&quot;, body: &quot;Rhoncus eleifend...&quot;},<br>      {title: &quot;Sed&quot;, body: &quot;Sed tellus...&quot;}<br>    ],<br>    <strong>currentItem: {}</strong><br>  },<br><strong>  methods: {<br>    setCurrentItem: function(item) {<br>      this.currentItem = item<br>    }<br>  }</strong><br>})</pre><p>Here we’re defining the initial state of the current item (an empty object), as well as defining the method to run when a user click’s on an item (which updates the current item). What’s nice is that the JavaScript here is only concerned about the data and how it transforms; there’s no code here that’s concerned with toggling the modal or manipulating the DOM.</p><p>This small example shows how Vue.js works well as a “progressive” framework — it’s very easy to add a script tag and write minimal HTML and JavaScript to get interactive features, and it plays well with other libraries to boot! If you’re a purist who believes that all the JavaScript should be written in Vue.js (instead of utilizing the JavaScript that comes with Bootstrap), there’s nothing stopping you from refactoring this code following all the best practices of a component based architecture — Vue.js handles that gracefully as well.</p><p>If you’re interested in learning more about using Vue.js with components, you can check out my <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-1-introduction-6cf3d49e42cf">Comparing Frontend Approaches</a> series, where I build the same app using various approaches, including <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-3-vue-js-8b8614e4f324">Vue.js</a> (using a simple script tag) versus <a href="https://medium.com/@peterxjang/comparing-frontend-frameworks-part-4-vue-js-with-components-675c880d4585">Vue.js using components</a> (using build tools like webpack). But for a quick prototype, it’s hard to beat a simple Vue.js script in conjunction with a visual framework like Bootstrap. Hope you find these tips useful on your journey!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2404efe93d6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/prototyping-with-vue-js-and-bootstrap-2404efe93d6">Prototyping with Vue.js and Bootstrap</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A minimalist guide to tmux]]></title>
            <link>https://medium.com/actualize-network/a-minimalist-guide-to-tmux-13675fb160fa?source=rss----bac00d3a330d---4</link>
            <guid isPermaLink="false">https://medium.com/p/13675fb160fa</guid>
            <category><![CDATA[tmux]]></category>
            <dc:creator><![CDATA[Peter Jang]]></dc:creator>
            <pubDate>Sat, 13 Jan 2018 22:33:02 GMT</pubDate>
            <atom:updated>2017-12-17T21:01:38.662Z</atom:updated>
            <content:encoded><![CDATA[<p>Let’s say you’re using <a href="https://medium.com/@peterxjang/how-to-learn-vim-a-four-week-plan-cd8b376a9b85">vim</a> to edit code on a remote computer <a href="https://medium.com/@peterxjang/web-development-on-an-ipad-69f253bc9c38">using ssh</a>, and you want to open a new terminal tab to start a development server. When you open a new tab, it won’t be connected to the remote machine, which means you would have to ssh and navigate to the appropriate directory each time. Furthermore, if your internet connection ever drops, you’ll lose all your ssh sessions and have to start everything up again.</p><p>You can solve these issues by using <a href="https://github.com/tmux/tmux/wiki">tmux</a>, a command line tool which provides the following key features:</p><ul><li>Enables multiple windows and panes within a single terminal window</li><li>Keeps windows and panes in a session (which stays alive even when the internet disconnects)</li><li>Enables session sharing (great for pair programming)</li></ul><h3>Getting started</h3><p>To install tmux, you can run sudo apt-get install tmux on Linux with <a href="https://help.ubuntu.com/community/AptGet/Howto">apt-get</a> or brew install tmux on a Mac with <a href="https://brew.sh/">homebrew</a>. Once it’s installed, you can create a new tmux session simply by running:</p><pre>$ tmux</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ml8e-M7seNIwHBOEN6wOrw.png" /></figure><p>This looks pretty much identical to the regular terminal, except there’s the green status bar at the bottom. From here we can start running tmux commands to manage terminal windows and sessions.</p><h3>Essential tmux commands</h3><p>Once you’re in tmux, you can run a command by entering a <strong>prefix key</strong> followed by a <strong>command key</strong>. By default, tmux uses Ctrl b as the prefix key. Note that you must let go of the prefix before entering the command key.</p><p>There are many tmux commands available, but here are the only ones you need to get started:</p><pre>&lt;prefix&gt; c: Create a new window (appears in status bar)</pre><pre>&lt;prefix&gt; 0: Switch to window 0</pre><pre>&lt;prefix&gt; 1: Switch to window 1</pre><pre>&lt;prefix&gt; 2: Switch to window 2 (etc.)</pre><pre>&lt;prefix&gt; x: Kill current window</pre><pre>&lt;prefix&gt; d: Detach tmux (exit back to normal terminal)</pre><p>If you kill all the windows in a tmux session, it will kill the overall session and return you to the normal terminal. If you use &lt;prefix&gt; d to detach tmux, you’ll be back in the normal terminal with your tmux session still running in the background. To see all tmux sessions, you can enter:</p><pre>$ tmux ls</pre><p>To attach to the last used session, you can enter:</p><pre>$ tmux a</pre><p>To attach to a specific session, you can enter:</p><pre>$ tmux a -t &lt;session-name&gt;</pre><p>And that’s it! Now you’re able to work on remote computers with multiple terminal windows and persistent sessions. You can even do pair programming by having two people attach to the same tmux session!</p><h3>Next steps</h3><p>The above commands are truly all you need to take advantage of the most important features of tmux. I try to keep things minimal, with one window for my text editor (vim), another window for a development server, and a third window for running any other commands.</p><p>Most people using tmux tend to use panes, which allows for multiple terminal views within a single window. You can do some crazy customizations with panes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZVmiTfLBYpTUdh-Tadx_SQ.png" /><figcaption>From <a href="https://www.reddit.com/r/unixporn/comments/689wfd/tmux_the_bridge/">reddit.com/r/unixporn</a></figcaption></figure><p>Panes are definitely cool and can be worth learning, but I would recommend holding off on them to start off to reduce the learning curve of tmux. When you use panes, you’ll need to memorize commands to create panes vertically and horizontally, navigate between panes, resize panes, destroy panes, etc. It will more than double the number of commands you need to memorize to get started! I would use tmux without panes for at least a week or two before trying out a new set of commands.</p><p>You can also customize tmux by making a .tmux.conf file in your home directory. Some common customizations include changing the prefix to Ctrl a (a little faster to type, doesn’t conflict with vim), start window numbering at 1 instead of 0 (again, a little faster to type), and customizing colors. If you are planning on rebinding the prefix key, then you should probably do that first so you build the right muscle memory. Here’s an absolutely minimal .tmux.conf file to change the prefix and window numbering:</p><pre># remap prefix from &#39;C-b&#39; to &#39;C-a&#39;<br>unbind C-b<br>set-option -g prefix C-a<br>bind-key C-a send-prefix</pre><pre># Start window numbering at 1<br>set -g base-index 1</pre><p>Again, tmux has a large number of options you can configure here, but to get started you want to keep it as minimal as possible. And once you’re ready to try out advanced features, don’t just blindly paste configurations you find online. It’s important that you understand each line you add to your .tmux.conf file!</p><p>Once you feel like you’ve mastered the basics, there are a ton of great resources out there to take your tmux game to the next level. However, to paraphrase Dr. Ian Malcolm, don’t be so preoccupied with whether or not you could that you don’t stop and think if you should. I would argue you get more than 80% of tmux’s advantages using the above minimal set of commands and customizations. Hope you find these tips useful on your journey!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=13675fb160fa" width="1" height="1" alt=""><hr><p><a href="https://medium.com/actualize-network/a-minimalist-guide-to-tmux-13675fb160fa">A minimalist guide to tmux</a> was originally published in <a href="https://medium.com/actualize-network">Actualize</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>