<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: TJ Fogarty</title>
    <description>The latest articles on DEV Community by TJ Fogarty (@teej).</description>
    <link>https://dev.to/teej</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F32695%2F9851f3c0-7eb7-47f8-98db-38cc26822036.jpg</url>
      <title>DEV Community: TJ Fogarty</title>
      <link>https://dev.to/teej</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/teej"/>
    <language>en</language>
    <item>
      <title>Text Selection Particle Effects</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Fri, 17 Apr 2020 07:31:41 +0000</pubDate>
      <link>https://dev.to/teej/text-selection-particle-effects-3b0j</link>
      <guid>https://dev.to/teej/text-selection-particle-effects-3b0j</guid>
      <description>&lt;p&gt;This was originally &lt;a href="https://tj.ie/text-selection-particle-effects/" rel="noopener noreferrer"&gt;posted on my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After reading &lt;a href="https://css-tricks.com/playing-with-particles-using-the-web-animations-api/" rel="noopener noreferrer"&gt;Playing With Particles Using the Web Animations API&lt;/a&gt; I wondered if it would be possible to create particle effects based on the user selecting text.&lt;/p&gt;

&lt;p&gt;You could whip up something quick by listening for a &lt;code&gt;mousedown&lt;/code&gt; event and adding the particles based on the mouse position. It looks cool, but I wasn't too happy because it doesn't look as neat, and it doesn't work if you're doing selection with the keyboard.&lt;/p&gt;

&lt;p&gt;There are two events we need to make this work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;selectstart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;selectionchange&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;selectstart&lt;/code&gt; is needed to reset our initial &lt;code&gt;top&lt;/code&gt; position. We use this to check if we're moving up or down in our selection. Once a selection is finished, and we start again, and the value can be reset.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;selectionchange&lt;/code&gt; is fired any time our selection changes, and we'll use this to generate our coordinates for our particles. This is where most of the work is done.&lt;/p&gt;

&lt;p&gt;Here's how it goes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We start selecting some text, and &lt;code&gt;selectstart&lt;/code&gt; is fired, where we reset our initial &lt;code&gt;top&lt;/code&gt; value to &lt;code&gt;null&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;selectionchange&lt;/code&gt; is then fired where get the current selection using &lt;code&gt;window.getSelection()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;With this, we get the range of text using &lt;code&gt;selection.getRangeAt(0)&lt;/code&gt;, followed by the bounds using &lt;code&gt;range.getClientRects()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The bounds now contain a &lt;code&gt;DOMRectList&lt;/code&gt; with our selections, and we're only interested in the first and last item in this list&lt;/li&gt;
&lt;li&gt;If our initial &lt;code&gt;top&lt;/code&gt; value isn't set (as it's been reset by &lt;code&gt;selectstart&lt;/code&gt;) then we assign it to the &lt;code&gt;top&lt;/code&gt; value of the first &lt;code&gt;DOMRect&lt;/code&gt; item in the list&lt;/li&gt;
&lt;li&gt;We check if the &lt;code&gt;left&lt;/code&gt; value has changed in the most recent &lt;code&gt;DOMRect&lt;/code&gt; item in the list&lt;/li&gt;
&lt;li&gt;We check if we're moving up or down the page with our selection&lt;/li&gt;
&lt;li&gt;If we're moving down, we use the most recent &lt;code&gt;DOMRect&lt;/code&gt; in the list, otherwise, we use the first&lt;/li&gt;
&lt;li&gt;If we're moving left, our &lt;code&gt;x&lt;/code&gt; position will be the &lt;code&gt;left&lt;/code&gt; value, otherwise, we'll use &lt;code&gt;right&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Our &lt;code&gt;y&lt;/code&gt; value will be the &lt;code&gt;y&lt;/code&gt; value of our chosen bounds, plus the height of the selection so the particles appear below the text&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thanks to &lt;a href="https://twitter.com/Mamboleoo" rel="noopener noreferrer"&gt;Louis Hoebregts&lt;/a&gt; for a great article. I had a bunch of fun and confusion getting this to work, but it was a welcome distraction.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tjFogarty/embed/mdJKNZM?height=600&amp;amp;default-tab=js,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>animation</category>
    </item>
    <item>
      <title>Image Parallax and Blur with Tornis</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Tue, 04 Jun 2019 15:35:22 +0000</pubDate>
      <link>https://dev.to/teej/image-parallax-and-blur-with-tornis-2883</link>
      <guid>https://dev.to/teej/image-parallax-and-blur-with-tornis-2883</guid>
      <description>&lt;p&gt;This was originally posted &lt;a href="https://tj.ie/image-parallax-and-blur-with-tornis/" rel="noopener noreferrer"&gt;on my site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Heads up - it's probably best to leave out the blur for larger things, as it &lt;em&gt;destroys&lt;/em&gt; performance on Firefox. The transform still works well.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;I updated my site recently and wanted to put a bit more focus on images. To do that, I included a space for cover images in articles. They take up a chunk of the top of the page, and I'll probably re-think this approach at some stage. Maybe I'll overlay the title and description over the image...&lt;/p&gt;

&lt;p&gt;I wanted to include a bit of pizazz, and the image was the prime candidate. That's when I remembered &lt;a href="https://tornis.robbowen.digital/" rel="noopener noreferrer"&gt;Tornis&lt;/a&gt;, a library that lets you respond to changes in the viewport. I wanted it for the scroll position monitoring, and while I can add an event listener to &lt;code&gt;window&lt;/code&gt; and do the same thing, I'm thinking of a few other use-cases for this library, so I figured why not give it a go.&lt;/p&gt;

&lt;p&gt;Here's a stripped down version of the code I used on my site. In reality, I'm checking for the existence of the cover image before kicking anything off. I followed the examples on the Tornis site to get here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;watchViewport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tornis&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;coverImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js-cover-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;scroll&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;scrollOffset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;coverImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientHeight&lt;/span&gt;

      &lt;span class="nx"&gt;scrollOffset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scrollOffset&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scrollOffset&lt;/span&gt;
      &lt;span class="nx"&gt;scrollOffset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scrollOffset&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scrollOffset&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--scrollY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scrollOffset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;watchViewport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateValues&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm checking to see if we've scrolled past the cover image and setting a value between 0 and 1 in the &lt;code&gt;scrollY&lt;/code&gt; custom property. With this custom property, I'm setting the &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;transform&lt;/code&gt; property of the custom image.&lt;/p&gt;

&lt;p&gt;I'm also using &lt;code&gt;position: sticky&lt;/code&gt; so the image travels down the page with us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.cover-image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sticky&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;240px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--scrollY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--scrollY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;-30px&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the blur, I'm multiplying &lt;code&gt;scrollY&lt;/code&gt; by 10 pixels. So when &lt;code&gt;scrollY&lt;/code&gt; is &lt;code&gt;0.3&lt;/code&gt; that'll evaluate to a &lt;code&gt;3px&lt;/code&gt; blur. The transform behaves in the same fashion so that it slowly crawls in the opposite direction of the scroll.&lt;/p&gt;

&lt;p&gt;You can probably think of more creative uses, for example, the amazing forest scrolling on the Tornis homepage.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tjFogarty/embed/qGyqwa/?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>parallax</category>
      <category>tornis</category>
      <category>scroll</category>
      <category>blur</category>
    </item>
    <item>
      <title>Task Management</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Thu, 23 May 2019 13:04:10 +0000</pubDate>
      <link>https://dev.to/teej/task-management-2kj0</link>
      <guid>https://dev.to/teej/task-management-2kj0</guid>
      <description>&lt;p&gt;This was originally posted &lt;a href="https://tj.ie/task-management/" rel="noopener noreferrer"&gt;on my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ask 10 people how they approach task management, and you might get 10 different answers. Not so much philosophically, but rather physically: how do you store or keep track of them?&lt;/p&gt;

&lt;p&gt;Over the years I thought that software would solve my problems with the sheer convenience of it all:&lt;/p&gt;

&lt;p&gt;I can sync tasks between my phone and laptop&lt;br&gt;
Less context-switching when I’m working&lt;br&gt;
Quick to tap/type out new tasks&lt;br&gt;
Categorisation of tasks&lt;br&gt;
Reminders&lt;br&gt;
Not trying to be dramatic or anything, but many of these advantages became problems when I applied it to my work.&lt;/p&gt;

&lt;p&gt;When I was starting out as a developer, I was hungry to learn and prove myself as a capable worker. Not bad things at all. But, when the day is done, it’s hard to switch off when anytime you pick up your phone you can see tasks waiting for you. I was thinking about the tasks waiting for me tomorrow, and going over what I worked on that day.&lt;/p&gt;

&lt;p&gt;This went on for a few years, and it got exhausting. It’s my own doing; no-one asked me to bring my work home with me (usually), and sometimes it’s necessary. As time went on I started working smarter and setting realistic expectations. This gave me some breathing room to think about task management, and I decided to switch up my approach.&lt;/p&gt;

&lt;p&gt;For my personal life, I still use software. I moved between Wunderlist and Todoist before settling on TickTick. They all do much the same thing. This works great for the features listed above, mostly for portability and access on the go. I keep track of shopping lists, article ideas, housework etc…&lt;/p&gt;

&lt;p&gt;For work, I use a physical journal. I use it to break the “conveniences” - everything stays in the journal, nothing leaks out. It’s like a quarantine of sorts. I appreciate that it makes me stop and think about what I’m doing, and I enjoy writing as well. Keeping up legible cursive writing is something I enjoy, especially when I write out a nice “f” - that can make my day.&lt;/p&gt;

&lt;p&gt;It also gives me a routine. I start every day away from the computer going over what waits for me. Once I check my emails and talk to people I’ll append new tasks to the list. At the end of the day, I’ll do up my tasks for tomorrow and carry over anything that didn’t get done. Closing the journal signals to my brain that work is done.&lt;/p&gt;

&lt;p&gt;I have a few journals filled up now, and it’s fun every now and then to look over them and see what I was up to this day 3 years ago. I can laugh at some notes I’ve written, or wince in pain at the tasks of a nightmare project. It reminds me how far I’ve come.&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>Derived Stores with Svelte</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Wed, 15 May 2019 12:49:52 +0000</pubDate>
      <link>https://dev.to/teej/derived-stores-with-svelte-4hc3</link>
      <guid>https://dev.to/teej/derived-stores-with-svelte-4hc3</guid>
      <description>&lt;p&gt;This was originally &lt;a href="https://tj.ie/derived-stores-with-svelte/" rel="noopener noreferrer"&gt;posted on my site.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt; has been getting a lot of attention recently, and rightly so. If you've used the likes of Vue or React in the past then Svelte won't be a million miles away. I recommend following &lt;a href="https://svelte.dev/tutorial/basics" rel="noopener noreferrer"&gt;their interactive tutorial&lt;/a&gt; if you're interested.&lt;/p&gt;

&lt;p&gt;Coming from a Vue background, one feature I love is computed properties. If you want to store some sort of value that depends on the reactive state, but you don't want to manually update it when state changes, this is where they come in. For example, a &lt;code&gt;greeting&lt;/code&gt; property could return something like &lt;code&gt;"Hello, ${this.name}!"&lt;/code&gt;. Vue knows to update &lt;code&gt;greeting&lt;/code&gt; whenever &lt;code&gt;name&lt;/code&gt; changes.&lt;/p&gt;

&lt;p&gt;Svelte offers a similar solution in what it calls derived stores. To show how this works, I created a small app that takes a JSON feed and lets us filter it. The feed contains a list of jobs, and I want to be able to search by job title, and only show remote jobs via a checkbox.&lt;/p&gt;

&lt;p&gt;The initial stores would be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svelte/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchTerm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;remoteOnly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At some stage, when the app is ready, the &lt;code&gt;jobs&lt;/code&gt; store is populated with an array of jobs from the feed. When I type in the search input the &lt;code&gt;searchTerm&lt;/code&gt; store is updated, and when I toggle the remote-only checkbox, the &lt;code&gt;remoteOnly&lt;/code&gt; store is toggled.&lt;/p&gt;

&lt;p&gt;Ideally what I'd like to do is avoid editing the &lt;code&gt;jobs&lt;/code&gt; store. I'd like to keep the original list untouched so I can return to the original state.&lt;/p&gt;

&lt;p&gt;This is where I can use derived stores. First I need to import it by updating the top-level import statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;derived&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svelte/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can declare my derived store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredJobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteOnly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;$jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$remoteOnly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$searchTerm&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;$jobs&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument is the store or stores, I wish to derive from. You only need to pass an array if you wish to use more than one. The second argument here is the callback, which gets passed the stores we asked for. At the moment we're returning the original &lt;code&gt;jobs&lt;/code&gt; store untouched. Let's create a function to show only the remote jobs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;filterByRemote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteOnly&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;remoteOnly&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredJobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteOnly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;$jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$remoteOnly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$searchTerm&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;filterByRemote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$remoteOnly&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A remote job here is any job that doesn't have a location set. If &lt;code&gt;remoteOnly&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt; we'll return the jobs array early.&lt;/p&gt;

&lt;p&gt;A similar approach is taken for the search term. It's not the most robust of searches, but it does the job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;filterByRemote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteOnly&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;remoteOnly&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;filterBySearchTerm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedTerm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedTerm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredJobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;remoteOnly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;searchTerm&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;$jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$remoteOnly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$searchTerm&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;filterBySearchTerm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filterByRemote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$remoteOnly&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;$searchTerm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we work from the inside-out, we'll see &lt;code&gt;filterByRemote&lt;/code&gt; returns an array of jobs, which then becomes the first argument in the call to &lt;code&gt;filterBySearchTerm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's my first experience with Svelte. Hopefully not the last as it's a refreshing take on what I've been doing previously.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/tjFogarty/project/details/XqqnVO" rel="noopener noreferrer"&gt;See the demo&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/tjFogarty/codepen-job-board-svelte" rel="noopener noreferrer"&gt;View the source&lt;/a&gt;&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>javascript</category>
      <category>state</category>
    </item>
    <item>
      <title>HTML Imports &amp; Component-Driven Development</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Sun, 16 Sep 2018 11:59:12 +0000</pubDate>
      <link>https://dev.to/teej/html-imports--component-driven-development-38cl</link>
      <guid>https://dev.to/teej/html-imports--component-driven-development-38cl</guid>
      <description>&lt;p&gt;This was originally &lt;a href="https://tj.ie/html-imports-component-driven-development/" rel="noopener noreferrer"&gt;posted on my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I was thinking about the process of building a site today vs maybe 8 years ago. When WordPress was called for, I'd usually build the site from start to finish within WordPress. You wouldn't see any CSS for a little while as custom post types needed creating, content needed to be sourced, and plugins needed to be installed. The whole front-end was mashed up with this configuration, and nothing was really finished until the site was live.&lt;/p&gt;

&lt;p&gt;Now, I didn't know a whole lot 8 years ago, so looking back there are ways it could have been done better. If I could go back, I'd probably build a static site first with all the parts, then port that over to WordPress when it was ready. It might be me, but I find not having to think about a CMS while doing initial front-end work very liberating. I don't want to troubleshoot why a template is breaking when I want to focus on styling. It's too easy for me to go off on a tangent and fiddle with PHP because I just thought of a better way to do something.&lt;/p&gt;

&lt;p&gt;Years later, this idea of focusing on the right things at the right time became, for me at least, encapsulated with &lt;a rel="noopener noreferrer" href="http://atomicdesign.bradfrost.com/"&gt;Atomic Design&lt;/a&gt;, so I want to take this moment to thank Brad Frost for preserving the few remaining brain cells I have left. I used tools like &lt;a rel="noopener noreferrer" href="https://patternlab.io/"&gt;Pattern Lab&lt;/a&gt;, and &lt;a rel="noopener noreferrer" href="https://fractal.build/"&gt;Fractal&lt;/a&gt; which really opened my eyes to the benefits of focusing on one thing at a time. &lt;/p&gt;

&lt;p&gt;Working on a team and incorporating these tools caused trouble at times. When I wanted to introduce this notion of building a project from components, everyone had to be on the same page, and have a solid understanding of the tools we were using. Technical bugs arose from misconfigured environments or an unmet expectation, and when you're moving fast with multiple projects, it can be a momentum-killer. &lt;/p&gt;

&lt;p&gt;In many cases, I see nothing wrong with setting up a local PHP server in a directory with &lt;code&gt;php -S localhost:8080&lt;/code&gt;, and setting up individual files for components and using &lt;code&gt;include&lt;/code&gt; to pull them into a template. Not all projects demand a batteries-included pattern library that can be exhibited to a client. Sometimes they don't care as long as the project gets done on time and within budget. It'd be nice sometimes to surprise a client with such a powerful resource packaged up in Pattern Lab, but when it's not called for we still get that nice component-y feeling from a smattering of PHP calls to other files. The developer still gets to focus on one thing at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML Imports
&lt;/h2&gt;

&lt;p&gt;I was wondering if there's any way we can replicate this in the browser without relying on external libraries. Maybe it's something that could be done with the tools we have today without depending too much on JavaScript to make a bunch of AJAX calls. That's when I was reminded of HTML Imports. I wonder how far along that is now...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0gwmfcz3o8sdn9wvqfh8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0gwmfcz3o8sdn9wvqfh8.png" alt="HTML Import support" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, &lt;a rel="noopener noreferrer" href="https://caniuse.com/#search=html%20imports"&gt;at the moment it's not great&lt;/a&gt;. Mozilla have &lt;a rel="noopener noreferrer" href="https://hacks.mozilla.org/2015/06/the-state-of-web-components/"&gt;published their thoughts on supporting it&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At Mozilla we want to explore how importing custom element definitions can align with upcoming ES6 module APIs. We’d be prepared to implement if/when they appear to enable developers to do stuff they can’t already do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/h-JwMiPUnuU/sl79aLoLBQAJ" rel="noopener noreferrer"&gt;Chrome will be removing the current implementation as well&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Still, though, I wanted to try to replicate that process of having components and including them where I needed them, without requiring a developer to know the ins-and-outs so they can focus on coding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Importing HTML
&lt;/h3&gt;

&lt;p&gt;Given I have an &lt;code&gt;index.html&lt;/code&gt;, a folder for partials (think a header and a footer which may be made up of components), and a folder for components, I want to load them, and inject them into the page. The first thing to do is register them in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of my document with &lt;code&gt;&amp;lt;link rel="import"&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"import"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"site-header"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"partials/site-header.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"import"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"primary-nav"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"components/navigation/primary-nav.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ID is important for referencing the imports in order to inject them into the page. So how do we reference those imports? I'm going to use a data attribute to define this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-import=&lt;/span&gt;&lt;span class="s"&gt;"site-header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside my site header, I'm also referencing the primary nav.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Logo&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-import=&lt;/span&gt;&lt;span class="s"&gt;"primary-nav"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;

&lt;p&gt;We need some JavaScript to get the ball rolling. We need to find the imports, grab their content, and replace instances of &lt;code&gt;[data-import]&lt;/code&gt; with their respective content. To be fair, it's not an awful lot of code, which was a nice surprise.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the import id to identify where to import into our document later on&lt;/li&gt;
&lt;li&gt;Grab the contents of the import. Since it'll include the body tag, we want to get everything inside that instead.&lt;/li&gt;
&lt;li&gt;Find the spots to put it in.&lt;/li&gt;
&lt;li&gt;Loop through each &lt;code&gt;data-import&lt;/code&gt; and replace it with the contents.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For step 4, I'm using &lt;code&gt;content.clondNode(true)&lt;/code&gt; rather than passing &lt;code&gt;content&lt;/code&gt;. I'm doing this because if there's more than one instance on the page, it'll only show the import in the last place it was referenced, essentially moving it around. By using &lt;code&gt;cloneDeep&lt;/code&gt;, and passing &lt;code&gt;true&lt;/code&gt; to include the children, we're copying it into every place it's being referenced.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;imports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link[rel="import"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlImport&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;htmlImport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// [1]&lt;/span&gt;
      &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;htmlImport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body *&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// [2]&lt;/span&gt;
      &lt;span class="nx"&gt;domTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[data-import="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// [3]&lt;/span&gt;

  &lt;span class="nx"&gt;domTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// [4]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I thought this was a fun little experiment, and it would be really cool to see this factor into the tooling of creating websites in the future. That's if the support is there. Otherwise, there are custom elements to look into, or I'm happy sticking with a static-like setup with PHP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://htmlimports.tj.ie" rel="noopener noreferrer"&gt;See the demo&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/tjFogarty/html-imports/" rel="noopener noreferrer"&gt;View the source&lt;/a&gt;&lt;/p&gt;

</description>
      <category>experiment</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Improving Client-Side Performance</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Mon, 10 Sep 2018 20:53:59 +0000</pubDate>
      <link>https://dev.to/teej/improving-client-side-performance-13h3</link>
      <guid>https://dev.to/teej/improving-client-side-performance-13h3</guid>
      <description>&lt;p&gt;This was originally posted &lt;a href="https://tj.ie/improving-client-side-performance/" rel="noopener noreferrer"&gt;on my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below are some ways I've found to optimise the performance of a site when getting it ready for a production environment, or patching an existing site. Where possible I'll list some common implementations, though understandably we don't always have the kind of access we want to make changes.&lt;/p&gt;

&lt;p&gt;These opportunities for client-side improvements are typically more common among websites rather than web apps. I think this is because of the tooling that comes with modern web app development e.g., &lt;a href="https://github.com/vuejs/vue-cli" rel="noopener noreferrer"&gt;vue-cli&lt;/a&gt; handles code-splitting, and generates a service worker. With build tools like webpack, or Gulp, it also means you can install a plugin to slot somewhere in the process that takes care of some of these. Others need some TLC, and a sympathetic ear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Images
&lt;/h2&gt;

&lt;p&gt;A picture paints a thousand words, but that's no good when many of those words are synonyms for "slow". People aren't always aware of the specifics of an image, nor should they have to be in some cases. If it's possible to upload a 5mb image, then chances are it'll happen at some point. That's just how it goes. &lt;/p&gt;

&lt;p&gt;There are some methods we can use to automate the reduction of overall bloat, and still preserve image quality. Where automation isn't always available (for things like static sites, or non-CMS based projects) there's still plenty of tools we can employ.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resize
&lt;/h3&gt;

&lt;p&gt;The first port of call is to resize the image. Generally speaking, we don't need a higher-than-usual-res image for something that only takes up a portion of the page, or isn't the reason for the page existing. If photography is your gig, then you can serve a smaller image with an option to view the high-res version if they so wish.&lt;/p&gt;

&lt;p&gt;If you do a search for "resize image" you'll find a whole host of online solutions that will do the job for you. This is great if you're only concerned with one or two images at any given time, but what if you have a folder full of them?&lt;/p&gt;

&lt;h4&gt;
  
  
  Batch Resize on MacOS
&lt;/h4&gt;

&lt;p&gt;MacOS comes with a program called Preview which lets you batch resize images. To do this, select all your images, right click and open with Preview.&lt;/p&gt;

&lt;p&gt;From here you'll see the preview window:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbz55errz5tpqc0624tu6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbz55errz5tpqc0624tu6.jpg" alt="Preview app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pressing CMD + A, you can select all the images, then go to Tools &amp;gt; Adjust Size. The dialogue that appears will let you set a value for the width and height, though you might only set the width and let it scale the image for you so no part of the picture is clipped.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjcmc9wfqabvnmjpycpk1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fjcmc9wfqabvnmjpycpk1.jpg" alt="Preview app resize dialogue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Batch Resize on Windows
&lt;/h4&gt;

&lt;p&gt;It's been a while since I've worked on a Windows machine, so pardon my ignorance here.&lt;/p&gt;

&lt;p&gt;One solution I've seen is to &lt;a href="https://www.easytechguides.com/resize-pictures.html" rel="noopener noreferrer"&gt;send your images as a mail attachment&lt;/a&gt;, at which point you'll be presented with an option to resize them and preserve their aspect ratio.&lt;/p&gt;

&lt;p&gt;Another, possibly more ideal, option is some software called &lt;a href="https://github.com/bricelam/ImageResizer" rel="noopener noreferrer"&gt;ImageResizer&lt;/a&gt;. When you select a group of images, and right click, it adds an option to resize from the context menu that appears.&lt;/p&gt;

&lt;h4&gt;
  
  
  CMS
&lt;/h4&gt;

&lt;p&gt;WordPress comes with &lt;a href="https://make.wordpress.org/core/2015/11/10/responsive-images-in-wordpress-4-4/" rel="noopener noreferrer"&gt;baked-in support for resizing images&lt;/a&gt;. If you're using Timber for WordPress (give it go, it's class) then you can also &lt;a href="https://timber.github.io/docs/reference/timber-imagehelper/#resize" rel="noopener noreferrer"&gt;resize images on the fly&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're using Craft CMS, you can &lt;a href="https://docs.craftcms.com/v3/image-transforms.html" rel="noopener noreferrer"&gt;resize, set the quality, and change the format&lt;/a&gt; of an image within your control panel and templates.&lt;/p&gt;

&lt;p&gt;For ExpressionEngine, a ridiculously powerful plugin is &lt;a href="https://docs.causingeffect.com/expressionengine/ce-image/index.html" rel="noopener noreferrer"&gt;CE Image.&lt;/a&gt; If you're working on a big project, then the asking price is worth it. Otherwise, there are some resize capabilities in the control panel.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cloud
&lt;/h4&gt;

&lt;p&gt;This approach, depending on your needs, could be a great way to manage images. If you're ok with storing your images with another service (provided you have your own copies backed up somewhere), then the free tier of &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt; will be quite appealing. Once you upload an image, you can manipulate the URL to make modifications such as resizing or converting to another format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimise
&lt;/h3&gt;

&lt;p&gt;If you haven't already gone the cloud or CMS route, then we still need to optimise things further.&lt;/p&gt;

&lt;p&gt;Once the images have been resized to your liking, the next step involves analysing them to identify any cruft that can be removed to reduce the file size. Some approaches include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removing metadata that might have been attached from the camera that took the original picture&lt;/li&gt;
&lt;li&gt;Optimising JPEGs to reduce the number of colours required to render the picture&lt;/li&gt;
&lt;li&gt;Converting from PNG to a lossy format if transparency isn't required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Long before software existed, we would have had to parlay with an image, and barter for pixels. We would have lengthy discussions trying to convince it that it looked just as good with 80 shades of red rather than 200. Or that JPEG suited it better than PNG. It was sparse times for all involved.&lt;/p&gt;

&lt;p&gt;Luckily, there are tools available to make this quick and simple. We're happy, and the images are happy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reduce File Size
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://imageoptim.com/mac" rel="noopener noreferrer"&gt;ImageOptim&lt;/a&gt; is my favourite for optimising images. You drag your images onto the program and it does the rest, replacing the existing ones by default. It lets you know how much it's saved as well. If you're not happy, you can update the preferences for more aggressive optimisation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fl6cqdejpj677ay82dvb8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fl6cqdejpj677ay82dvb8.jpg" alt="ImageOptim app results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ImageOptim website has &lt;a href="https://imageoptim.com/versions.html" rel="noopener noreferrer"&gt;a page that suggests alternatives for other platforms&lt;/a&gt;. Again, apologies for my MacOS-centric state of mind.&lt;/p&gt;

&lt;h4&gt;
  
  
  Convert PNG to JPEG
&lt;/h4&gt;

&lt;p&gt;If your images don't need transparency, you can convert them to JPEG and reap the benefits of a smaller file size.&lt;/p&gt;

&lt;p&gt;On MacOS you can open your images in Preview, as before, then after selecting everything you can go to File &amp;gt; Export Selected Items. In the dialogue that appears, in the bottom left is a button labelled Options. From here you can select a different format.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0no4lhhz4fa1y3ufw6a7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F0no4lhhz4fa1y3ufw6a7.jpg" alt="Converting PNG to JPEG with the Preview app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's also plenty of online tools that do this. On Windows, an option might be some software like &lt;a href="https://www.irfanview.com/" rel="noreferrer noopener"&gt;IrfanView&lt;/a&gt; which I &lt;em&gt;believe&lt;/em&gt; lets you batch convert images. I don't know if I've said this yet Windows users, but I'm sorry. I haven't even apologised to Linux users yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responsive
&lt;/h3&gt;

&lt;p&gt;Making your images responsive involves more than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sure, it resizes, but you could be wasting a lot of bandwidth here. If I need a sticky note I don't fold an A4 sheet into a small square; that would be a waste of paper. &lt;/p&gt;

&lt;p&gt;If we follow the approaches above, we can combine different sized images and serve the appropriate one to the user.&lt;/p&gt;

&lt;p&gt;To do this, we can either use the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element, or the &lt;code&gt;srcset&lt;/code&gt; attribute. With &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; you're giving &lt;em&gt;instructions&lt;/em&gt; to the browser to render a specific image at a given breakpoint. With &lt;code&gt;srcset&lt;/code&gt; you're giving &lt;em&gt;information&lt;/em&gt; to the browser, and trust it to deliver the most appropriate image in any given case. The two can be used together as well for greater control.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; is great for cases like art direction, when on a smaller screen the image might be too small to make sense of, so you want to serve a different image, or a cropped version of the original, to preserve the original meaning.&lt;/p&gt;

&lt;p&gt;An example from the Responsive Image Community Group website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 40em)"&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"big.jpg 1x, big-hd.jpg 2x"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; 
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"small.jpg 1x, small-hd.jpg 2x"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"fallback.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;srcset&lt;/code&gt; attribute means you have less control over what the user ends up seeing, so it's a good idea to keep this reserved for resized images. It could be dependent on the size of their screen, or even their network capabilities.&lt;/p&gt;

&lt;p&gt;And another example from the Responsive Image Community Group website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"small.jpg"&lt;/span&gt;
     &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"large.jpg 1024w, medium.jpg 640w, small.jpg 320w"&lt;/span&gt;
     &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 36em) 33.3vw, 100vw"&lt;/span&gt;
     &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A rad wolf"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information I'd recommend checking out the... &lt;a href="https://responsiveimages.org/" rel="noopener noreferrer"&gt;Responsive Images Community Group website&lt;/a&gt;, as they cover usage and use cases in greater detail. &lt;a href="https://www.smashingmagazine.com/2014/05/responsive-images-done-right-guide-picture-srcset/" rel="noopener noreferrer"&gt;Smashing Magazine&lt;/a&gt; also has a good overview of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy Loading
&lt;/h3&gt;

&lt;p&gt;So you have your images resized and optimised with your responsive image tags in place. What else could we possibly do? Well, it's not guaranteed a user will see every image on the page, so why make them download everything? Take for example a carousel. Now, I know people have opinions, and I'm gonna stay clear of that. You do you. But how often does someone see every slide of a carousel? We could instead load the image when the new slide arrives.&lt;/p&gt;

&lt;p&gt;The same goes for scrolling, not everyone makes it to the bottom of a page for one reason or another.&lt;/p&gt;

&lt;p&gt;By far my favourite solution is &lt;a href="https://github.com/verlok/lazyload" rel="noopener noreferrer"&gt;LazyLoad&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;  &lt;span class="na"&gt;data-src=&lt;/span&gt;&lt;span class="s"&gt;"../img/44721746JJ_15_a.jpg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- If JavaScript isn't available --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"../img/44721746JJ_15_a.jpg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;myLazyLoad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LazyLoad&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;elements_selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.lazy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works for the &lt;code&gt;srcset&lt;/code&gt; attribute in &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; as well. &lt;/p&gt;

&lt;h2&gt;
  
  
  Third-Party Plugins/Scripts
&lt;/h2&gt;

&lt;p&gt;You'd be surprised how many external scripts are loaded on some websites for various tasks. Some are unavoidable, such as analytics, but others aren't strictly necessary for the functionality that they provide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sharing Widgets
&lt;/h3&gt;

&lt;p&gt;This is a common one, where someone wants there to be buttons available to share a page on any social media platform they want, then and there. You don't need an external service firing JavaScript into your site to do that, though.&lt;/p&gt;

&lt;p&gt;You can make it works with HTML, and either use the supplied CSS, or use your own to blend it in with the rest of the site. For this, you can use &lt;a href="https://sharingbuttons.io/" rel="noopener noreferrer"&gt;Sharingbuttons.io&lt;/a&gt;, or &lt;a href="https://simplesharingbuttons.com/" rel="noopener noreferrer"&gt;Simple Sharing Buttons&lt;/a&gt;. Both offer a clean approach, while the latter also has a JavaScript version which will automatically gather the relevant page information to share. If you're using a content management system, you should get by without it by populating the sharing URLs with information on the current page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Social Media Feeds
&lt;/h3&gt;

&lt;p&gt;OK, this one is a bit more involved... you can include social media feeds without JavaScript, but you need to have some sort of server-side access to render them. You can roll your own using a given social media's SDK, or use a plugin. The added bonus of this is being able to style it to match your site.&lt;/p&gt;

&lt;p&gt;I don't have any recommendations on this part, as I ended up rolling my own for Craft CMS, ExpressionEngine, and WordPress for work projects. I used the PHP SDKs for Twitter and Instagram. So that can be a fun learning experience.&lt;/p&gt;

&lt;p&gt;It's more straight-forward with WordPress, as you can create a PHP file and include it in your &lt;code&gt;functions.php&lt;/code&gt; file. For Twitter, I used the &lt;a href="https://github.com/J7mbo/twitter-api-php" rel="noopener noreferrer"&gt;twitter-api-php&lt;/a&gt; package for gathering the latest tweets of an account.&lt;/p&gt;

&lt;p&gt;I'm sure there are solutions out there for your CMS of choice, however, and I'll leave it to you to find one that does the job. This isn't an easy-out, I just don't have experience with those plugins. Hopefully, it's sparked some investigation on ways you can speed up your projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundling Assets
&lt;/h2&gt;

&lt;p&gt;If you're not using webpack, Gulp, Parcel etc... for packaging all of your dependencies, then you may see extra requests happening in your browser. In my experience, this happens mostly with WordPress sites where plugins drop their own styles and scripts into your templates.&lt;/p&gt;

&lt;p&gt;WordPress users rejoice, you can choose from many solutions such as &lt;a href="https://wordpress.org/plugins/autoptimize/" rel="noopener noreferrer"&gt;Autoptimize&lt;/a&gt;, and &lt;a href="https://wordpress.org/plugins/w3-total-cache/" rel="noopener noreferrer"&gt;W3 Total Cache&lt;/a&gt; which brings more than just combining assets to the table.&lt;/p&gt;

&lt;p&gt;I'm not aware of up to date plugin solutions for other platforms, but I'll add them here if I come across any.&lt;/p&gt;

&lt;p&gt;If you're using npm, a quick solution might be to use the &lt;a href="https://github.com/mishoo/UglifyJS2" rel="noopener noreferrer"&gt;UglifyJS&lt;/a&gt; package. So after running &lt;code&gt;npm install uglify-js&lt;/code&gt;, you can add a new &lt;code&gt;script&lt;/code&gt; to your &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uglifyjs src/vendor/jquery.js src/main.js -o dist/bundle.min.js -c -m"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command &lt;code&gt;npm run build&lt;/code&gt; then would combine and minify your JavaScript. The &lt;code&gt;-c&lt;/code&gt;, and &lt;code&gt;-m&lt;/code&gt; flags will compress and mangle your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Workers
&lt;/h2&gt;

&lt;p&gt;Using service workers, we can intercept network requests and serve up cached assets. What we can do is, once the user has visited the site, cache things like styles, scripts, fonts etc... then any subsequent requests from that user will be served from the cache. Once we make updates, we can invalidate the service worker, and fresh assets will be downloaded again.&lt;/p&gt;

&lt;p&gt;For this site at the moment, I'm using the &lt;a href="https://github.com/goldhand/sw-precache-webpack-plugin" rel="noopener noreferrer"&gt;SW Precache webpack plugin&lt;/a&gt;. This lets me generate a service worker with webpack when I move things into production. I'm finding it's especially useful for fonts to avoid a flash of unstyled text. &lt;/p&gt;

&lt;p&gt;Here's the configuration from my site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SWPrecacheWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;cacheId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tj-ie-v3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;minify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;stripPrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;static&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;staticFileGlobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;static/fonts/*.woff2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;static/fonts/*.woff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;static/js/main.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;static/css/main.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;static/js/chunks/*.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This greatly improves performance, though if you make some drastic changes a user might see a broken site until they reload. Maybe there's a solution for that, but I haven't gotten that far yet in my service worker career.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/web/fundamentals/primers/service-workers/" rel="noopener noreferrer"&gt;Google has an introduction&lt;/a&gt; that's worth looking at, if you want to dig deeper and understand how it fully works (like I didn't).&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus Research
&lt;/h2&gt;

&lt;p&gt;While writing this I became aware of &lt;a href="https://progressivetooling.com/" rel="noopener noreferrer"&gt;Progressive Tooling&lt;/a&gt; which looks to be a pretty fine resource for tooling around this subject.&lt;/p&gt;

&lt;p&gt;If you want to eek out more performance improvements, have a look at these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developers.google.com/web/updates/2016/03/link-rel-preload" rel="noopener noreferrer"&gt;link rel="preload"&lt;/a&gt; lets you identify priority assets to improve loading performance&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; makes use of a global CDN to improve performance. It has a generous free offering as well, which makes it worth trying out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all I have! I'll update this with any new insights I receive, but hopefully there are a few bits here that prove useful.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>frontend</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Building a Table of Contents with the Intersection Observer API</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Sat, 08 Sep 2018 16:48:04 +0000</pubDate>
      <link>https://dev.to/teej/building-a-table-of-contents-with-the-intersection-observer-api-3b3g</link>
      <guid>https://dev.to/teej/building-a-table-of-contents-with-the-intersection-observer-api-3b3g</guid>
      <description>&lt;p&gt;This was originally &lt;a href="https://tj.ie/building-a-table-of-contents-with-the-intersection-observer-api/"&gt;posted on my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While planning out an upcoming post, I noticed there was quite a lot of content to cover with no easy way to navigate it. So, rather than actually write the post, I went off on a tangent and built a table of contents component. Great, back to writing... ah it'd be nice if it followed the user scrolling down the page. OK, done. Wait, it would be cool if I could highlight the currently active section...&lt;/p&gt;

&lt;p&gt;What I ended up with is by no means perfect; it doesn't highlight correctly when scrolling up until you reach a new heading. However, I think it does a decent job for now without using JavaScript to wrap each section in an identifiable container, and I can always improve it, or remove it entirely if it's not fit for purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;I'm using Hugo to generate my site which comes with a useful tag to automatically create a table of contents. This works by finding all the headings, assigning them an id, and generating a list of links.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableOfContents&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To begin with, we need a document with some headings. These headings should have an id so we can use anchor tags to jump to that section of the document. We also need a list of links with the &lt;code&gt;href&lt;/code&gt; pointing to each heading. The idea is that the basic functionality works without JavaScript enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;aside&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Table of Contents&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#first-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;First Heading&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; 
      &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#second-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Second Heading&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; 

        &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#third-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Third Heading&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; 
          &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Article Title&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

    ... 

    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"first-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;First Heading&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    ...
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"second-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Second Heading&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    ...
    &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"second-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Third Heading&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
    ... and so on
  &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The lists in the navigation are nested to reflect the hierarchy of the content. A h3 lives within the section defined by a h2, a h4 by a h3 etc...&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the JavaScript Object
&lt;/h2&gt;

&lt;p&gt;I'm sure you clever folks can make a cleaner job of this. I opted to avoid any plugins in an effort to learn more about what's going on, as well as keep the bundle size of my scripts down. That means there's no fallback support for browsers that don't support the Intersection Observer API. As of this writing, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Browser_compatibility" rel="noopener noreferrer"&gt;that means Internet Explorer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first step I figured was to collect the list of links and headings in an article. I did this wrapped in an object to try and organise the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TableOfContents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js-toc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// this is the container for our links&lt;/span&gt;
  &lt;span class="na"&gt;links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findLinksAndHeadings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="nx"&gt;findLinksAndHeadings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;links&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The reason I'm destructuring into an array here: &lt;code&gt;this.links = [...this.container.querySelectorAll('a')]&lt;/code&gt; is because &lt;code&gt;querySelectorAll&lt;/code&gt; will return a Node List as opposed to an array which we can call &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;forEach&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; etc... on.&lt;/p&gt;

&lt;p&gt;Then I &lt;code&gt;map&lt;/code&gt; over the links, find the &lt;code&gt;href&lt;/code&gt; and use that to collect the heading it links to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intersection Observer
&lt;/h3&gt;

&lt;p&gt;Before we got the Intersection Observer API, we would have attached an event listener to the window object on scroll. Then we'd check the current scroll position of the page, and measure it against the &lt;code&gt;offsetTop&lt;/code&gt; of the element we wanted to check was in view. We could still do this, but scrolling can be expensive unless we debounce the listener. In any case, we'd be reaching into the DOM to pull out values, manually check them, and carry on. &lt;/p&gt;

&lt;p&gt;With this API, we can minimize the effort required by learning a slightly different way of approaching the detection of elements in the viewport.&lt;/p&gt;

&lt;p&gt;Let's take a look at a stripped back example that I've copied from the MDN link above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#article&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;rootMargin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;From MDN:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h4&gt;
  
  
  root
&lt;/h4&gt;

&lt;p&gt;The element that is used as the viewport for checking visibility of the target. Must be the ancestor of the target. Defaults to the browser viewport if not specified or if null.&lt;/p&gt;
&lt;h4&gt;
  
  
  rootMargin
&lt;/h4&gt;

&lt;p&gt;Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). The values can be percentages. This set of values serves to grow or shrink each side of the root element's bounding box before computing intersections. Defaults to all zeros.&lt;/p&gt;
&lt;h4&gt;
  
  
  threshold
&lt;/h4&gt;

&lt;p&gt;Either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed. If you only want to detect when visibility passes the 50% mark, you can use a value of 0.5. If you want the callback run every time visibility passes another 25%, you would specify the array [0, 0.25, 0.5, 0.75, 1]. The default is 0 (meaning as soon as even one pixel is visible, the callback will be run). A value of 1.0 means that the threshold isn't considered passed until every pixel is visible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With that information, I used the following options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;rootMargin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I left out &lt;code&gt;root&lt;/code&gt; as I wanted it to default to the browser viewport. I didn't need anything extra from &lt;code&gt;rootMargin&lt;/code&gt;, and &lt;code&gt;threshold&lt;/code&gt; made sense to activate for when 100% of the element is visible.&lt;/p&gt;

&lt;p&gt;For the observer itself, I needed to write a function for the callback, and watch the headings for when they become visible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleObserver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intersectionOptions&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;handleObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;links&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// I needed to use &amp;gt; 1 on Codepen, not sure why?&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intersectionRatio&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previousSection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// I'll tell you in a minute&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highlightFirstActive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// in a minute I said&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So I create the observer, observe each heading, and check if any heading is in view. If it's in view, I give it a class, of &lt;code&gt;is-visible&lt;/code&gt;, otherwise, I remove it. Scrolling up and down the page now I can see, via classes on the links, which headings are currently in view. Now to style the most appropriate one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding the Active Section
&lt;/h3&gt;

&lt;p&gt;This was a &lt;em&gt;really&lt;/em&gt; fun one. It's my fault for skipping breakfast that it took so long to figure this out. While multiple headings might be visible at once, I only wanted the first available one to show an active state. That's straightforward enough with &lt;code&gt;document.querySelector&lt;/code&gt; which will return the first match it finds. The problem I was having was that as soon as the heading went out of view, the active style would be removed even though I was in that relevant section of the document.&lt;/p&gt;

&lt;p&gt;The breakthrough moment was keeping track of the previously active section. With this, I could say "OK, if there's nothing active at the moment, default to the most recently active section." That's what &lt;code&gt;this.previousSection&lt;/code&gt; is doing for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;highlightFirstActive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;firstVisibleLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.is-visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;links&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstVisibleLink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;firstVisibleLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;firstVisibleLink&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previousSection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`a[href="#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previousSection&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I'm grabbing the first visible link, then removing all other &lt;code&gt;is-active&lt;/code&gt; classes. After this, I check if we actually have a visible section (we might be in between sections with no headings visible). If we find one, great; add the class and bail. If we don't find one, and we have a previous section, then that's what we highlight instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;See the Pen &lt;a href="https://codepen.io/tjFogarty/pen/yxPdqe/"&gt;Scroll spy table of contents&lt;/a&gt; by T.J. Fogarty (&lt;a href="https://codepen.io/tjFogarty"&gt;@tjFogarty&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's It So Far
&lt;/h2&gt;

&lt;p&gt;I would be lying if I said this was well-thought out. It was a tangent within a tangent. I spent more time on this post than I did writing the code, and I'm sure there are things I've missed that could greatly simplify it. Give me a shout if you spot anything and I'll make amendments.&lt;/p&gt;

&lt;p&gt;In the final version, I also introduced smooth scrolling to items provided the user doesn't mind by checking for the &lt;code&gt;prefers-reduced-motion&lt;/code&gt; media query. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>intersectionobserver</category>
    </item>
    <item>
      <title>Feedback on my website</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Tue, 28 Aug 2018 16:36:49 +0000</pubDate>
      <link>https://dev.to/teej/feedback-on-my-website-55n2</link>
      <guid>https://dev.to/teej/feedback-on-my-website-55n2</guid>
      <description>&lt;p&gt;Heya, I'm currently trying to build up my website. I've been trying to post more regularly this year, and I've started fleshing out the content more in terms of projects.&lt;/p&gt;

&lt;p&gt;I'd greatly appreciate any feedback as I'm not a designer. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://tj.ie/" rel="noopener noreferrer"&gt;https://tj.ie/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please feel free to share your own websites, or work you're proud of. It'd be great to get some inspiration, and celebrate the magic work you do!&lt;/p&gt;

</description>
      <category>feedback</category>
      <category>help</category>
    </item>
    <item>
      <title>Fathom Analytics on Heroku</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Sun, 26 Aug 2018 21:01:37 +0000</pubDate>
      <link>https://dev.to/teej/fathom-analytics-on-heroku-3o6h</link>
      <guid>https://dev.to/teej/fathom-analytics-on-heroku-3o6h</guid>
      <description>&lt;p&gt;This was originally &lt;a href="https://tj.ie/fathom-analytics/" rel="noopener noreferrer"&gt;posted on my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've been going without any sort of analytics for my website for a number of months as I just wasn't happy with giving all that information to third parties. Plus, this is a fairly small operation here, so it's not exactly a requirement of mine.&lt;/p&gt;

&lt;p&gt;That being said, I've been keeping an eye on &lt;a href="https://usefathom.com/" rel="noopener noreferrer"&gt;Fathom Analytics&lt;/a&gt; as it looks like an ideal solution:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It tracks users on a website (without collecting personal data) and give you a non-nerdy breakdown of your top content and top referrers. It does so with user-centric rights and privacy, and without selling, sharing or giving away the data you collect. It's a simple and easy to use for website owners at any technical level.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/usefathom/fathom/blob/master/pkg/api/collect.go#L47" rel="noopener noreferrer"&gt;Looking at the source&lt;/a&gt;, it also respects the "do not track" preference of the user.&lt;/p&gt;

&lt;p&gt;I've been holding off on giving it a go because I had no idea how to install it, until a nice person on Github gave &lt;a href="https://github.com/usefathom/fathom/issues/59#issuecomment-413469001" rel="noopener noreferrer"&gt;a quick run-through of how to get it running on Heroku&lt;/a&gt;. I'm trialing it using a Hobby dyno which costs $7 a month, so if it all works out I'll happily front that cost.&lt;/p&gt;

&lt;p&gt;You'll need a &lt;a href="https://www.heroku.com/" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; account, and the &lt;a href="https://devcenter.heroku.com/articles/heroku-cli" rel="noopener noreferrer"&gt;Heroku CLI&lt;/a&gt; to follow along. You'll need to have your billing details filled in as well as it makes use of addons for getting a database. You don't have to use a Hobby dyno either if you just want to see what's involved.&lt;/p&gt;

&lt;p&gt;So without further ado, the commands are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get the code&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/usefathom/fathom.git
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;fathom

&lt;span class="c"&gt;# Create a new heroku app&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;heroku create

&lt;span class="c"&gt;# Push the fathom container to heroku's registry&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;heroku container:push web

&lt;span class="c"&gt;# Release the container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;heroku container:release web

&lt;span class="c"&gt;# Configure fathon to use msql and a secret&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;heroku config:set &lt;span class="nv"&gt;FATHOM_DATABASE_DRIVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql &lt;span class="nv"&gt;FATHOM_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;whateverGeneratedSecretYouWantToUse

&lt;span class="c"&gt;# Create a new mysql database&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;heroku addons:create jawsdb:kitefin

&lt;span class="c"&gt;# Get the mysql connection url&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;heroku config:get JAWSDB_URL //mysql://user:password@host:3306/database_name

&lt;span class="c"&gt;# Set database variables from the above url&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;heroku config:set &lt;span class="nv"&gt;FATHOM_DATABASE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;database_name &lt;span class="nv"&gt;FATHOM_DATABASE_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt; &lt;span class="nv"&gt;FATHOM_DATABASE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt; &lt;span class="nv"&gt;FATHOM_DATABASE_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"host:3306"&lt;/span&gt;

&lt;span class="c"&gt;# Register a new user&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;heroku run ./fathom register &lt;span class="nt"&gt;--email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;something@email.com &lt;span class="nt"&gt;--password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'yourpassword'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To clarify a comment when running &lt;code&gt;heroku config:get JAWSDB_URL ...&lt;/code&gt;, what it gives back is what you plug into the next command when setting the Fathom environment variables in the same format &lt;code&gt;//mysql://user:password@host:3306/database_name&lt;/code&gt;. I know it kinda explains it already, but I missed it because I was too eager.&lt;/p&gt;

&lt;p&gt;After that, it's almost ready. The last step is to link to the tracking script as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Fathom - simple website analytics - https://github.com/usefathom/fathom --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementsByTagName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fathom-script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;//your-app-name.herokuapp.com/tracker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fathom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;fathom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trackPageview&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- / Fathom --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd say it took about 15 minutes to get up and running, which is pretty good going for me. I'm looking forward to what the future holds for Fathom.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>heroku</category>
    </item>
    <item>
      <title>A Signal in the Static (moving to a static site)</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Sun, 19 Aug 2018 19:25:10 +0000</pubDate>
      <link>https://dev.to/teej/a-signal-in-the-static-moving-to-a-static-site-2icn</link>
      <guid>https://dev.to/teej/a-signal-in-the-static-moving-to-a-static-site-2icn</guid>
      <description>&lt;p&gt;About a year ago I set out to rebuild my site, and being reared on PHP I opted to go for Craft CMS while it was in beta. I figured it'd be interesting to learn it while it was still in development, and maybe create my own plugins. &lt;/p&gt;

&lt;p&gt;Haha. Jokes. I did not. I tried, though, but never really needed anything extra. &lt;/p&gt;

&lt;p&gt;I was also using Laravel Forge to automatically deploy whenever I pushed any changes to the master branch. To this day it's still a nice approach, but there's a lot of moving parts. I have a repo, a server that needs to be looked after (like when I updated PHP and broke everything), a database that needs to be backed up, and a CMS with some plugins that need to be kept up to date. Granted, I could not have touched it after day 1 and it would still work just the same, but that's not me. If it ain't broke, go fix it.&lt;/p&gt;

&lt;p&gt;That's where the allure of a static site generator comes in. As far as the site goes, everything is in one place. Content lives in Markdown files which makes things pretty portable. I landed on using &lt;a href="https://gohugo.io/" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt; after seeing some recommendations for it, and in about a day I had my entire site copied over, including CSS and JS. To copy the content I took advantage of the &lt;a href="https://github.com/craftcms/element-api" rel="noopener noreferrer"&gt;Element API&lt;/a&gt; plugin for Craft to get a JSON object of all my posts. The config file for the plugin looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;craft\elements\Entry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;craft\helpers\UrlHelper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'endpoints'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'posts.json'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s1"&gt;'elementType'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'criteria'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'section'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'posts'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s1"&gt;'transformer'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Entry&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'date_published'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;postDate&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\DateTime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ATOM&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="s1"&gt;'slug'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'body'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;postContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'categories'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when it hit &lt;code&gt;https://my-site.com/posts.json&lt;/code&gt; it returned everything I needed. I saved this to a file to quickly generate markdown versions like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./data.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildCategory&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`- &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildFrontMatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`---
title: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/"/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;"
date: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date_published&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
draft: false
categories: \n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buildCategory&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`\n`&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
---`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;buildFrontMatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;

  &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`../path/to/final/content/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.md`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The file was saved!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt; 
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I needed to run &lt;code&gt;sudo node index.js&lt;/code&gt; on that as it needed to create files, but I didn't have to touch the generated markdown files once they were in the right folder.&lt;/p&gt;

&lt;p&gt;Once that was done, I skipped on over to &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; and hooked it up. I needed to do some configuration before everything would work. Mainly this was down to my assets living in a theme which was in a subfolder. To let Netlify know about it, I had to create a new &lt;code&gt;package.json&lt;/code&gt; in the root with a &lt;code&gt;build&lt;/code&gt; command that did the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[build]&lt;/span&gt;
&lt;span class="py"&gt;publish&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"public"&lt;/span&gt;
&lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hugo --minify &amp;amp;&amp;amp; npm run build"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the build command in my root &lt;code&gt;package.json&lt;/code&gt; dug into the folder it needed and ran some more commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd ./themes/nua &amp;amp;&amp;amp; npm install &amp;amp;&amp;amp; npm run production"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, it was smooth sailing. Limerick just won the All-Ireland senior hurling final so I'm gonna grab a beer. That makes two victories today. Cheers, agus Luimneach Abú!!!&lt;/p&gt;

</description>
      <category>cms</category>
      <category>craft</category>
      <category>hugo</category>
      <category>static</category>
    </item>
    <item>
      <title>What Do I Know?</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Tue, 24 Jul 2018 21:42:03 +0000</pubDate>
      <link>https://dev.to/teej/what-do-i-know-5575</link>
      <guid>https://dev.to/teej/what-do-i-know-5575</guid>
      <description>&lt;p&gt;A couple of years back, I began to let the waves of new tech wash over me, knowing the tide would go back out again. I started to relax a bit more, and accept that the stuff I knew was enough for what I needed. "What's the rush?" For that, I count myself lucky. &lt;/p&gt;

&lt;p&gt;That's not to say I stopped learning new things -- far from it -- but I came to accept that I'll never know everything. I started with HTML, CSS, and JavaScript over 10 years ago, and I'm still using them today. I'll probably be using them for the next 10, too. All the while I'm tacking on new bits of experience and knowledge. Slowly but surely.&lt;/p&gt;

&lt;p&gt;I found my work journal from a previous job, and it kinda shocked me the number of things I was doing on a given day. I never really stopped to assess what it would all lead towards, or how I could eventually spend that experience to sell my skills. For example, on one day I was working on 3 different projects, troubleshooting with clients, reviewing code, attending meetings, and writing a spec for an upcoming job. That's a lot of stuff I would cast off as "just work", before spending my nights tangled in envy at all the cool stuff people were building in the open-source world. I was envious because I filled all my social feeds with such amazing people, and I compared myself to them. &lt;/p&gt;

&lt;p&gt;I'm not an internet-famous developer. I'm not a Vue or React expert. I haven't created a library that's used by thousands of developers.&lt;/p&gt;

&lt;p&gt;But I know stuff. &lt;/p&gt;

&lt;p&gt;If I hadn't kept a log of my days, I don't think I would have realised how much I've been exposed to and developed from. I can't underestimate the value of writing stuff down with pen and paper. I don't need to charge it, I don't need to be connected to the internet. All I need is a few quiet minutes to reflect on my journey thus far. I can build a piece of software, and not understand every software engineering principle, but I'll learn some more of them eventually with practice. Just because I can't put an exact name to all the stuff I know, doesn't mean I don't know it, if that makes sense. Know what I mean?&lt;/p&gt;

</description>
      <category>career</category>
      <category>life</category>
    </item>
    <item>
      <title>How do you organise personal projects?</title>
      <dc:creator>TJ Fogarty</dc:creator>
      <pubDate>Wed, 04 Jul 2018 18:40:41 +0000</pubDate>
      <link>https://dev.to/teej/how-do-you-organise-personal-projects-306m</link>
      <guid>https://dev.to/teej/how-do-you-organise-personal-projects-306m</guid>
      <description>&lt;p&gt;I usually start coding straight away, but I'm starting to get a bit more organised because I'm finding I lose interest shortly thereafter. &lt;/p&gt;

&lt;p&gt;How do you organise your personal projects? Do you treat them like a client project with deadlines, and write documents on requirements? What helps you through a personal project beyond the desire/motivation to create something?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
  </channel>
</rss>
