<?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>Forem: 𒎏Wii 🏳️‍⚧️</title>
    <description>The latest articles on Forem by 𒎏Wii 🏳️‍⚧️ (@darkwiiplayer).</description>
    <link>https://forem.com/darkwiiplayer</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%2F119538%2F280c8527-9b9c-420b-9d09-f9b69d16fc3b.png</url>
      <title>Forem: 𒎏Wii 🏳️‍⚧️</title>
      <link>https://forem.com/darkwiiplayer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/darkwiiplayer"/>
    <language>en</language>
    <item>
      <title>Controller Registry: adding behaviour to any HTML elements</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Mon, 11 Aug 2025 19:21:32 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/controller-registry-adding-behaviour-to-any-html-elements-3co8</link>
      <guid>https://forem.com/darkwiiplayer/controller-registry-adding-behaviour-to-any-html-elements-3co8</guid>
      <description>&lt;h2&gt;
  
  
  Web Components
&lt;/h2&gt;

&lt;p&gt;Custom elements are cool; I use them for plenty of things, but there is one thing about them that makes them unsuited for many problems: they take full control over an element and correspond to that element's main purpose.&lt;/p&gt;

&lt;p&gt;This is perfectly reasonable for actual components, when one wants to define a completely new type of element with one clear functionality.&lt;/p&gt;

&lt;p&gt;They don't, however, allow combining more than one custom element in one HTML tag, and while builtin custom elements are technically a thing, in practice they aren't viable (thanks, safari) and so attaching custom behaviours to built-in HTML elements isn't possible in practice either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Controllers
&lt;/h2&gt;

&lt;p&gt;Or traits, or whatever else one wants to call them.&lt;/p&gt;

&lt;p&gt;The idea here is simple: instead of the custom behaviour existing in the tag itself, it exists in an external object (or other entity), which is attached to an HTML tag and controls it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controller-Registry
&lt;/h2&gt;

&lt;p&gt;As a prototype to play around with this idea, I implemented &lt;code&gt;controller-registry&lt;/code&gt; (&lt;a href="https://git.but.gay/darkwiiplayer/controller-registry" rel="noopener noreferrer"&gt;repository&lt;/a&gt;, &lt;a href="https://github.com/darkwiiplayer/controller-registry/" rel="noopener noreferrer"&gt;github mirror&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The API is similar to custom elements wherever it makes sense, but is still different in many ways due to the differing requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  In summary
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In HTML, controllers work like classes: one attribute that's a whitespace-separated list&lt;/li&gt;
&lt;li&gt;In JavaScript, there is one central registry associating names to behaviours&lt;/li&gt;
&lt;li&gt;Adding a controller to an element's attribute attaches a new controller automatically, while defining a new controller automatically upgrades all elements that already have that name in their list.&lt;/li&gt;
&lt;li&gt;Unlike custom elements, controllers can also be removed from elements (and re-added, as many times as desired)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Playground
&lt;/h3&gt;

&lt;p&gt;Here's a codepen I prepared from the example file in the repository for anyone who wants to just play around with it before reading on.&lt;/p&gt;

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

&lt;p&gt;Feel free to also open up the dev tools and manually tinker with the &lt;code&gt;controller&lt;/code&gt; attribute of the elements; their behaviours should be updated automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;All things considered, the implementation is surprisingly simple.&lt;/p&gt;

&lt;p&gt;Most of what matters happens in the &lt;a href="https://git.but.gay/darkwiiplayer/controller-registry/src/commit/8c243adb8dfa879eb531fd6d88e1ae5c533a6b24/src/controller-registry.js#L80" rel="noopener noreferrer"&gt;&lt;code&gt;ControllerRegistry&lt;/code&gt;&lt;/a&gt; class, and its one global instance. Creating new registries is possible, but I won't focus on that here.&lt;/p&gt;

&lt;p&gt;The class itself holds a couple of internal data structures to keep track of elements and controllers. Weak maps are really convenient here: they allow associating data with an object but still let the object be garbage-collected if it doesn't have any other references.&lt;/p&gt;

&lt;p&gt;The registry also has a &lt;a href="https://git.but.gay/darkwiiplayer/controller-registry/src/commit/8c243adb8dfa879eb531fd6d88e1ae5c533a6b24/src/controller-registry.js#L81" rel="noopener noreferrer"&gt;&lt;code&gt;MutationObserver&lt;/code&gt;&lt;/a&gt; that listens for both DOM insertions to keep track of new nodes as well as attribute changes specifically on the &lt;code&gt;controller&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;How much optimization happens here is up to the browser, but using the &lt;code&gt;attributeFilter&lt;/code&gt;, I've at least given the browser all the information it needs to treat this attribute the same way it does &lt;code&gt;class&lt;/code&gt;, and internally hook into it without even considering where in the DOM it happens. That's as much as an implementation in JS can achieve.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://git.but.gay/darkwiiplayer/controller-registry/src/commit/8c243adb8dfa879eb531fd6d88e1ae5c533a6b24/src/controller-registry.js#L128" rel="noopener noreferrer"&gt;&lt;code&gt;define&lt;/code&gt;&lt;/a&gt; function is mostly quite simple, in that it just takes its arguments as key and value to insert into a map; but before doing that it also checks if any element is already waiting for a controller of that name to be defined so that can be attached immediately.&lt;/p&gt;

&lt;p&gt;A little bit of extra code at the start of the method checks for functions that are constructors, and transforms them into a wrapper function that calls the constructor with &lt;code&gt;new&lt;/code&gt; and the element as its argument, then waits for the controller to be detached again and tries calling a &lt;code&gt;detach&lt;/code&gt; method on the controller object if it is defined. Technically the constructor only receives a revocable proxy to the element, but I am not sure if I want to keep that in the code.&lt;/p&gt;

&lt;p&gt;Some of the functions simply mimic the public API of &lt;code&gt;CustomElementRegistry&lt;/code&gt;; keeping things similar to what developers already know reduces friction, after all.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://git.but.gay/darkwiiplayer/controller-registry/src/commit/8c243adb8dfa879eb531fd6d88e1ae5c533a6b24/src/controller-registry.js#L172" rel="noopener noreferrer"&gt;&lt;code&gt;update&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://git.but.gay/darkwiiplayer/controller-registry/src/commit/8c243adb8dfa879eb531fd6d88e1ae5c533a6b24/src/controller-registry.js#L187" rel="noopener noreferrer"&gt;&lt;code&gt;attach&lt;/code&gt;&lt;/a&gt; methods are where most of the magic happens:&lt;/p&gt;

&lt;p&gt;It extracts controller names from the &lt;code&gt;controller&lt;/code&gt; attribute of the elements, then looks those up in the controller list and either attaches them or adds the element to the waiting list if a controller by that name is not yet defined.&lt;/p&gt;

&lt;p&gt;When a controller is attached, a little bit more magic happens: A new &lt;a href="https://git.but.gay/darkwiiplayer/controller-registry/src/commit/8c243adb8dfa879eb531fd6d88e1ae5c533a6b24/src/controller-registry.js#L197" rel="noopener noreferrer"&gt;promise&lt;/a&gt; is created that fulfills when the controller is detached. An &lt;a href="https://git.but.gay/darkwiiplayer/controller-registry/src/commit/8c243adb8dfa879eb531fd6d88e1ae5c533a6b24/src/controller-registry.js#L195" rel="noopener noreferrer"&gt;abort controller&lt;/a&gt; is also created which will abort on disconnect. Its signal is then saved as a &lt;code&gt;signal&lt;/code&gt; property on the promise object. In fact, the abort controller is what resolves the promise in an event listener.&lt;/p&gt;

&lt;p&gt;This means that the promise can also be passed as the third argument to &lt;code&gt;element.addEventListener&lt;/code&gt; to automatically remove the listener when the controller is detached. Pretty neat, isn't it? You can see this in the codepen example above.&lt;/p&gt;

&lt;p&gt;And that is most of the exciting parts. At the top of the module there is also a helper that works a bit like a &lt;code&gt;DomTokenList&lt;/code&gt;, so controllers can be manipulated just like classes; but its implementation isn't very complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;All in all, the proof of concept took a couple of hours to implement and maybe two or three more of tweaking, documenting and playing around with it.&lt;/p&gt;

&lt;p&gt;What do you think? Should this be a native feature of browsers? Are custom elements already enough? They can totally be combined, by the way: you can add controllers to custom elements too :)&lt;/p&gt;

&lt;p&gt;Whatever your thought, let me know in the comments, and feel free to pick the code apart.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>components</category>
      <category>vanilla</category>
    </item>
    <item>
      <title>Up and down again: A missing feature in front end event propagation?</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Sun, 06 Oct 2024 07:51:20 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/up-and-down-again-a-missing-feature-in-front-end-event-propagation-3g5b</link>
      <guid>https://forem.com/darkwiiplayer/up-and-down-again-a-missing-feature-in-front-end-event-propagation-3g5b</guid>
      <description>&lt;p&gt;JavaScript and the DOM use the Event API to let elements inform their ancestors of things that happen. This works well because each element only ever has exactly one parent (up to the document root), so the path an event takes is linear and reasonably short relative to the size of the document.&lt;/p&gt;

&lt;p&gt;But there is no analogue mechanism to propagate events downwards through the DOM:&lt;/p&gt;

&lt;p&gt;For a simple example, one can imagine a scenario like 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;product-search&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;search-box&amp;gt;&amp;lt;/search-box&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;product-card&amp;gt;&amp;lt;/product-card&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/product-search&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API of the &lt;code&gt;search-box&lt;/code&gt; element is easy to build with generic DOM APIs: whenever it internally determines that a user's input should start a search, it emits an event of some sort, presumably something like &lt;code&gt;framework:search&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;product-search&lt;/code&gt; can then simply listen for said event on itself and automatically becomes very resilient to nesting and other weird HTML shenanigans; users can even use different search components if they're feeling extra creative that day.&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;product-search&lt;/code&gt; component has received the search event, it needs to propagate the state change back down to the &lt;code&gt;product-card&lt;/code&gt;. This is surprisingly difficult to do using a generic mechanism.&lt;/p&gt;

&lt;p&gt;The problem here is that a) &lt;code&gt;product-search&lt;/code&gt; might not want to limit itself to only &lt;code&gt;product-card&lt;/code&gt; children but work with any element that conforms to some generic API, b) the element that needs the notification might be deeply nested within the &lt;code&gt;product-search&lt;/code&gt;, and c) the component might have a large number of children, making a shotgun approach risky from a performance perspective.&lt;/p&gt;

&lt;p&gt;As far as I am aware, there is no feature in the DOM API that fully addresses this. Am I just making things too complicated here? Is there an easy way around it?&lt;/p&gt;

&lt;p&gt;My best idea here is a compromise that achieves a) and b) by using &lt;code&gt;querySelectorAll("*")&lt;/code&gt; and dispatching a non-bubbling event on every child element indiscriminately.&lt;/p&gt;

&lt;p&gt;What do y'all think?&lt;/p&gt;




&lt;p&gt;One thought I forgot to add is the possibility to shift the responsibility for the communication to the &lt;code&gt;product-card&lt;/code&gt;; it could, when connected, inspect its own context and install an event listener on &lt;code&gt;product-search&lt;/code&gt;. This ends up causing the same problems as before: We might want to use a different wrapper, the search wrapper might be at any point in the ancestor chain and installing event listeners everywhere might degrade perforamance &lt;em&gt;and&lt;/em&gt; respond to events the component actually doesn't care about.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>discuss</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I write CSS selectors</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Mon, 02 Sep 2024 19:29:17 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/how-i-write-css-selectors-kfd</link>
      <guid>https://forem.com/darkwiiplayer/how-i-write-css-selectors-kfd</guid>
      <description>&lt;p&gt;There's many CSS methodologies out there, and I hate them all. Some more (tailwind &amp;amp; al.) and some less (BEM, OOCSS, etc.). But at the end of the day, they all have flaws.&lt;/p&gt;

&lt;p&gt;People use these approaches for good reasons, of course, and many of the problems addressed are ones I have also encountered. So in this post, I'd like to write down my own guidelines for how I keep CSS organised.&lt;/p&gt;

&lt;p&gt;This isn't a fully described CSS methodology that anyone could just start using. Maybe it could be turned into one with some extra work, but the purpose of this post is simply to show how I make these decisions when writing CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Builtin Elements
&lt;/h2&gt;

&lt;p&gt;As a rule of thumb, I try to use builtin element types as much as possible, and with as little extra fluff as possible.&lt;/p&gt;

&lt;p&gt;Having a need for a thousand different types of button is a sign that there might be something wrong with the design on a deeper level, so while in some cases I get the appeal of CSS being inert until framework-specific classes are used, in most cases I see it as ideal when a button is just &lt;code&gt;&amp;lt;button&amp;gt;Click Me!&amp;lt;/button&amp;gt;&lt;/code&gt; and looks like a button without any further magic.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;code&gt;div.btton&lt;/code&gt; should turn into &lt;code&gt;button&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Elements
&lt;/h2&gt;

&lt;p&gt;Not all design elements have a semantically fitting HTML equivalent, and for these cases, I usually resort to custom elements.&lt;/p&gt;

&lt;p&gt;I haven't seen many instances of custom element names being used without any accompanying javascript, but it has proven to be a surprisingly solid choice for writing clear HTML that also looks the way I want.&lt;/p&gt;

&lt;p&gt;Entirely separate elements in terms of design are also more likely to, over time, develop requirements that can only be implemented with JavaScript, which leaves you with a clear path to implementing those that doesn't need any changes in the HTML nor the CSS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;code&gt;div.vsep&lt;/code&gt; should turn into &lt;code&gt;vertical-separator&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Classes
&lt;/h2&gt;

&lt;p&gt;Classes should work as modifiers of the existing node name, rather than entirely new element types, and often have similar but different effects on different element types.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A dangerous button is a &lt;code&gt;button.danger&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Attributes
&lt;/h2&gt;

&lt;p&gt;Some ways of modifying elements aren't simple on/off switches that classes are useful for, but behave more like key-value pairs.&lt;/p&gt;

&lt;p&gt;In these cases, custom attributes with matching selectors have proven to be the best option almost every time I've used them. Unlike hyphenated classes, they show on a syntax-level which is the attribute and which is the value, making it easier for editors to highlight them, easier for the human eye to quickly parse, and easier to interface using JavaScript.&lt;/p&gt;

&lt;p&gt;For those of us who still have hope that the &lt;code&gt;attr()&lt;/code&gt; function might one day make its way into CSS for more than just &lt;code&gt;content&lt;/code&gt;, this is also an extra layer of future-proofing.&lt;/p&gt;

&lt;h2&gt;
  
  
  IDs
&lt;/h2&gt;

&lt;p&gt;IDs are, by definition, unique inside the document. As such, any rule targeting a particular ID will be limited and may require refactoring if it later turns out there should be more than one element of this type on the lage after all.&lt;/p&gt;

&lt;p&gt;As such, IDs should be used sparingly and only when it makes no sense to ever have more than one element in one document.&lt;/p&gt;

&lt;p&gt;The benefits over classes in both a practical and readability sense are rather small, so erring on the side of classes is usually the best idea when no clear 1-to-1 relation between element and styling can be identified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inline CSS
&lt;/h2&gt;

&lt;p&gt;Any real-world application will at some point have elements that simply require individual tweaking in order to make them more aesthetically pleasing in the context they appear in.&lt;/p&gt;

&lt;p&gt;In these cases, the &lt;code&gt;style&lt;/code&gt; attribute is the way to go. Any reason why using it is considered bad practice applies to any sort of inline-styling, including utility classes. The problem isn't the attribute, it's mixing styling and markup.&lt;/p&gt;

&lt;p&gt;The one difference between &lt;code&gt;style&lt;/code&gt; and &lt;code&gt;class&lt;/code&gt; for inlining styles is that one indicates purpose, allows using plain CSS and is mostly universal, while the other isn't.&lt;/p&gt;

&lt;p&gt;Put simply, &lt;code&gt;width: 100px&lt;/code&gt; has a universally defined meaning, whille &lt;code&gt;.width-100&lt;/code&gt; could mean anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Utility Classes
&lt;/h2&gt;

&lt;p&gt;In very rare occasions, element-specific styles get so complex that inlining them explicitly would harm readability, or is even impossible (for example if it requires media queries).&lt;/p&gt;

&lt;p&gt;In these cases, utility classes are basically the only option, even if they're ugly.&lt;/p&gt;

&lt;p&gt;In an ideal world, these could be handled separately from specific mixin classes, and I have even considered using prefixing to more easily tell them apart, but ultimately haven't found a good way to make these not be ugly.&lt;/p&gt;

&lt;p&gt;I like the idea of prefixing utility classes with a &lt;code&gt;+&lt;/code&gt; to represent that they &lt;em&gt;add&lt;/em&gt; some sort of functionality to the element, in contrast to normal classes, that specify what type of  an element is.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;button class="danger +floating"&amp;gt;Red floating button&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;And that was about it. Of course, no two projects are the same, and sometimes rules have to be bent a little to remain practical, but overall, that's my framework for deciding how to make a thing on the screen look a certain way.&lt;/p&gt;

&lt;p&gt;What are your thoughts? Do you hate it? Do you think it makes sense? Let me know in a comment 💜&lt;/p&gt;

</description>
      <category>rant</category>
      <category>css</category>
    </item>
    <item>
      <title>What's on your desk that makes it *your* desk?</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Mon, 02 Sep 2024 13:47:29 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/whats-on-your-desk-that-makes-it-your-desk-lj8</link>
      <guid>https://forem.com/darkwiiplayer/whats-on-your-desk-that-makes-it-your-desk-lj8</guid>
      <description>&lt;p&gt;Most of us spend a lot of time at our desks. So I'm curious, what do y'all have at your desks that truly makes it &lt;em&gt;your&lt;/em&gt; desk? Anything goes, no matter how fancy or mundane it is!&lt;/p&gt;

&lt;p&gt;I recently treated myself to this semi-cheap tea set, because sometimes it's nice to just take my head out of the code, make some nice tea, and just get a bit of a mental reset before continuing with work.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxem3lyx25rg1zh0ntce.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxem3lyx25rg1zh0ntce.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>discuss</category>
      <category>selfcare</category>
    </item>
    <item>
      <title>CSS needs a name prefix selector</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Tue, 27 Aug 2024 08:30:24 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/css-needs-a-name-prefix-selector-34e5</link>
      <guid>https://forem.com/darkwiiplayer/css-needs-a-name-prefix-selector-34e5</guid>
      <description>&lt;p&gt;I love working with custom elements for all kinds of things, but sometimes I just want to hide stuff until it is loaded or do something else with it in CSS.&lt;/p&gt;

&lt;p&gt;A simple solution looks like this:&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;framework-button&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:defined&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put that in a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag in the HTML and the button will just appear once it's loaded.&lt;/p&gt;

&lt;p&gt;But with larger frameworks, this becomes really annoying. Sometimes you can get away with simply selecting &lt;code&gt;:not(:defined)&lt;/code&gt;, but that's not always viable.&lt;/p&gt;

&lt;p&gt;An obvious (at least to me) fix: CSS needs a name prefix selector, so you can just do this&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;framework-&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:defined&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not like this would be an entirely new thing. We can already kind of do this with attribute selectors.&lt;/p&gt;

&lt;p&gt;And I'm sure allowing splits only at &lt;code&gt;-&lt;/code&gt; in an element's name would make it reasonably easy to implement this effectively in browsers too.&lt;/p&gt;

&lt;p&gt;What do y'all think? Would this be useful? Are there easier solutions that already work?&lt;/p&gt;

</description>
      <category>css</category>
      <category>discuss</category>
      <category>web</category>
    </item>
    <item>
      <title>Faking sessionStorage to keep sites from crashing</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Fri, 10 May 2024 08:15:49 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/faking-sessionstorage-to-keep-sites-from-crashing-31ih</link>
      <guid>https://forem.com/darkwiiplayer/faking-sessionstorage-to-keep-sites-from-crashing-31ih</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;So I have cookies disabled by default in my browser. The reason should be obvious: some sites just have no business saving data on my PC. Yes yes, cookies are cool for safety and performance stuff, but site owners couldn't handle their toy responsibly, so I'm taking it away.&lt;/p&gt;

&lt;p&gt;Now, the problem is that the modern web isn't built for privacy-valuing users. Sites often use frameworks intended for highly interactive applications, even when all they do is display static content.&lt;/p&gt;

&lt;p&gt;So I often end up seeing pages like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhp37cozpqbtshlsoju1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhp37cozpqbtshlsoju1.png" alt="Image description" width="773" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is nextjs.org, by the way. Looking into the console, here's what happens:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5sdiinprn9dno0n9eax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5sdiinprn9dno0n9eax.png" alt="Image description" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cool. This site doesn't really "do" anything, I just want to read some text and ideally have some images in between. You know, a classic static website. And yet, it wants storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Okay, challenge accepted.&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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&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="s2"&gt;sessionStorage&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="nf"&gt;get&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="mi"&gt;20&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="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool, looks like chromium lets me overwrite this property. And considering there's a flicker before the error appears, I bet I can inject a fix using an extension before the application even has a chance to fail.&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;fakeSessionStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* TODO: Implement */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&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="s2"&gt;sessionStorage&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="nf"&gt;get&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;fakeSessionStorage&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;Wait, but this is going to reset sessionStorage on every website, even if I enable cookies manually. &lt;em&gt;Ooooops&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Guess I'll have to wrap the whole snippet in some more code:&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;try&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="nx"&gt;sessionStorage&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// inject custom sessionStorage&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks good. What does the website do now?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqbh33kdony0rdvtg2rz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbqbh33kdony0rdvtg2rz.png" alt="Image description" width="794" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! Looks like I'm making progress. Now I face a different problem: I actually need to fake the &lt;code&gt;sessionStorage&lt;/code&gt; object somehow.&lt;/p&gt;

&lt;p&gt;What... what does that even do? &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Storage" rel="noopener noreferrer"&gt;MDN to the rescue&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;It looks like the list of methods I'd have to fake is somewhat reasonable.&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="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Honestly, this looks like a &lt;code&gt;Map&lt;/code&gt;. The functions are just named differently, but that's about it. I just have to map (no pun intended) the methods to each other like this:&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="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;             &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then just re-implement the &lt;code&gt;key&lt;/code&gt; method using Map's &lt;code&gt;keys&lt;/code&gt; method. Sounds easy :)&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;And after a bit of tinkering, here's the entirety of my resulting code:&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;class&lt;/span&gt; &lt;span class="nc"&gt;FakeStorage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;map&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;Map&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

   &lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nf"&gt;clear&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="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&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;fakeLocalStorage&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;FakeStorage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;try&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="nx"&gt;sessionStorage&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&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="s2"&gt;sessionStorage&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="nf"&gt;get&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;fakeLocalStorage&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 inject this into the page using the "User JavaScript and CSS" chrome extension, but I'm sure most other extensions will work too.&lt;/p&gt;

&lt;p&gt;And voilà:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30zfmeszddmodeif9c83.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30zfmeszddmodeif9c83.png" alt="Image description" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A working next.js website without any pesky cookies. Would it have been so hard to build this fallback into their page in the first place? 😩&lt;/p&gt;




&lt;p&gt;Note: I have not thoroughly tested my fake storage object. Don't just copy-paste this into your application without making sure it doesn't have any subtle bugs.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>localstorage</category>
    </item>
    <item>
      <title>CSS Hack: "Named Layers"</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Fri, 03 May 2024 10:49:12 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/css-hack-named-layers-1ap7</link>
      <guid>https://forem.com/darkwiiplayer/css-hack-named-layers-1ap7</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When elements overlap on a website, the CSS property &lt;code&gt;z-index&lt;/code&gt; lets us decide which should be drawn on top.&lt;/p&gt;

&lt;p&gt;But this property only uses plain numbers, which will usually appear spread out through countless CSS files.&lt;/p&gt;

&lt;p&gt;This is both difficult to keep track of and hard to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Stop using numbers directly when setting the &lt;code&gt;z-index&lt;/code&gt; on a selector. Instead, put something like this at an easy to find spot, like a separate &lt;code&gt;layers.css&lt;/code&gt; file:&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="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="py"&gt;--layer-overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="py"&gt;--layer-floating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&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;Then, when you want to assign one of these "layers" to an element, simply use the custom element as its &lt;code&gt;z-index&lt;/code&gt;:&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;.overlay&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;z-index&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;--layer-overlay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;my-floating-component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;z-index&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;--layer-floating&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 way, you can just number your layers starting at one, without having to leave gaps in case you ever want to add something in between. The numbers are now all in one place and easy to update.&lt;/p&gt;

&lt;p&gt;Say you want to add a &lt;code&gt;header&lt;/code&gt; layer in between the existing two. You just increase the overlay layer to 3 and add the header in between:&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="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="py"&gt;--layer-overlay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="py"&gt;--layer-header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="py"&gt;--layer-floating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&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;&lt;strong&gt;Note&lt;/strong&gt;: &lt;em&gt;This is different from "CSS Layers", which change the order in which rules are applied. Those are "layers" in the cascade, these are "layers" in the rendered webpage.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've been using custom properties for many years now, and only recently figured out I can just use them to organise my z-indices like this.&lt;/p&gt;

&lt;p&gt;Did you think of doing this too? Have you seen it out in the wild? Please leave your thoughts in the comments &amp;lt;3&lt;/p&gt;

</description>
      <category>css</category>
      <category>hack</category>
      <category>zindex</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Sh*t, I didn't mean to commit that!</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Fri, 05 Apr 2024 10:40:15 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/sht-i-didnt-mean-to-commit-that-221b</link>
      <guid>https://forem.com/darkwiiplayer/sht-i-didnt-mean-to-commit-that-221b</guid>
      <description>&lt;h2&gt;
  
  
  We've all been there 😖
&lt;/h2&gt;

&lt;p&gt;When working on a project, specially in a team, and doubly so if there's other things that occasionally distract us from the task at hand, it is easy to commit changes that were meant to stay local.&lt;/p&gt;

&lt;p&gt;One may add a line that wipes a database at the start of an import for a tighter testing cycle, add debug outputs, etc.&lt;/p&gt;

&lt;p&gt;As life gets in the way, we take breaks, coworkers want other things from us, or we simply have our head filled with other aspects of the task, one can easily forget to remove these things before committing and pushing.&lt;/p&gt;

&lt;h2&gt;
  
  
  But it doesn't have to be this way 😮
&lt;/h2&gt;

&lt;p&gt;Git hooks can help us with this. We just have to think up a way for us to mark changes that should never make it into a commit and write a hook to detect them.&lt;/p&gt;

&lt;p&gt;Here, I will use the string "nocommit": if this appears anywhere in the changes I'm going to commit, I want git to reject my commit and tell me where it was found.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get to work 😀
&lt;/h2&gt;

&lt;p&gt;Writing git hooks is surprisingly easy: They're really just shell scripts that run in the project directory. Some get arguments, but we don't need them here.&lt;/p&gt;

&lt;p&gt;We care about the &lt;code&gt;pre-commit&lt;/code&gt; hook. To enable it, just write a file to &lt;code&gt;.git/hooks/pre-commit&lt;/code&gt; in your git repository and set its executable permission. To make it reject all commits, put this in the file:&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;#/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To accept all commits, just have it return 0 instead.&lt;/p&gt;

&lt;p&gt;Now, to get to the interesting part:&lt;/p&gt;

&lt;h3&gt;
  
  
  Get our files 📂
&lt;/h3&gt;

&lt;p&gt;For starters, we'll need access to the files as they will get committed. We don't want our entire commit to fail if a there's a file with a &lt;code&gt;nocommit&lt;/code&gt; mark that isn't even going to get committed.&lt;/p&gt;

&lt;p&gt;Luckily, this is relatively easy to achieve. We can check out the index of the repository into a temporary directory like this:&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="nv"&gt;tmp_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /dev/shm &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"git-index.XXXX"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
git checkout-index &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmp_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command just gives us a temporary directory in &lt;code&gt;/dev/shm&lt;/code&gt; and makes sure it is unique.&lt;/p&gt;

&lt;p&gt;The second command checks out the entire index into this temporary directory.&lt;/p&gt;

&lt;p&gt;But there isn't really much of a point in checking every file in the repository; we can just limit our search go the files that have been modified. To list these, we can use this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;--bytes&lt;/span&gt; 4-
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get our marks 🔖
&lt;/h3&gt;

&lt;p&gt;Now comes the juicy part. Having our files, we want to scan them for a keyword. For better readability, we can put this part in a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;get_marks&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;--bytes&lt;/span&gt; 4- | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;file
    &lt;span class="k"&gt;do
        if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmp_dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; nocommit &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmp_dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/:/ /"&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;line
            &lt;span class="k"&gt;do
                &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;done
        fi
    done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start with listing the files as described above, then pipe that into a &lt;code&gt;sh&lt;/code&gt; while loop that reads each individual line.&lt;/p&gt;

&lt;p&gt;We then use the &lt;code&gt;grep&lt;/code&gt; command to search for "nocommit" in our target &lt;code&gt;$file&lt;/code&gt; in the &lt;code&gt;$tmp_dir&lt;/code&gt; directory, ignoring case (&lt;code&gt;-i&lt;/code&gt;) and prepending the line number (&lt;code&gt;-n&lt;/code&gt;). The result then gets piped though &lt;code&gt;sed&lt;/code&gt; to turn the single colon &lt;code&gt;:&lt;/code&gt; separating the line number from the line into a normal space. This last part is just a matter of taste, honestly.&lt;/p&gt;

&lt;p&gt;The results are then again piped into another loop, which simply prints the file name and the line. The resulting lines would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;path/to/file.js:14 console.log("I hate my job") // nocommit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Evaluate the results 🔍
&lt;/h3&gt;

&lt;p&gt;Now that we can extract the lines we care about, it's time to decide if the commit is good or not.&lt;/p&gt;

&lt;p&gt;We can do this with a simple &lt;code&gt;if&lt;/code&gt;:&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="nv"&gt;marks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_marks&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$marks&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
    /bin/echo &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1b[31m'no&lt;/span&gt;&lt;span class="nv"&gt;$THEWORD&lt;/span&gt;&lt;span class="s2"&gt;' mark(s) found:&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1b[0m"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$marks&lt;/span&gt;
    &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we call our function to get the marks and save its output into a &lt;code&gt;marks&lt;/code&gt; variable. If it's empty, we set a status variable to 0. We can't exit yet because we still need to clean up after ourselves. If &lt;code&gt;marks&lt;/code&gt; isn't empty, we print a warning, then the marks, and set our status to 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleanup 🧹
&lt;/h3&gt;

&lt;p&gt;After this we're basically done with the important part; but the temporary directory is still around and we should delete it now that we're done with it. After that, we can just exit with the status we decided on earlier.&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="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$tmp_dir&lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  And there you have it 😁
&lt;/h2&gt;

&lt;p&gt;Here's the finished script. I made one more modification to make sure the script itself can actually be committed without problem 😅&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;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;THEWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commit

&lt;span class="nv"&gt;tmp_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /dev/shm &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"git-index.XXXX"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
git checkout-index &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmp_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/

get_marks&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;--bytes&lt;/span&gt; 4- | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;file
    &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; no&lt;span class="nv"&gt;$THEWORD&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmp_dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/:/ /"&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;line
        &lt;span class="k"&gt;do
            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;done
    done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;marks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_marks&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$marks&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
    /bin/echo &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1b[31m'no&lt;/span&gt;&lt;span class="nv"&gt;$THEWORD&lt;/span&gt;&lt;span class="s2"&gt;' mark(s) found:&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1b[0m"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$marks&lt;/span&gt;
    &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$tmp_dir&lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
      <category>shell</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Title: Top 20 JavaScript One-Liners That Don't Actually Work</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Thu, 15 Feb 2024 10:37:26 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/title-top-20-javascript-one-liners-that-dont-actually-work-30mj</link>
      <guid>https://forem.com/darkwiiplayer/title-top-20-javascript-one-liners-that-dont-actually-work-30mj</guid>
      <description>&lt;p&gt;Generative AI has blessed the world with many listicles, some helpful, some less so. But with all that focus on having AI do actual work, we are missing out on the fun side: Making AI do dumb stuff. So for this post, I asked ChatGPT to give me 20 JavaScript one-liners that &lt;em&gt;don't&lt;/em&gt; actually work.&lt;/p&gt;

&lt;p&gt;This post has been copied as-is from ChatGPT with two changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I added some notes in between using quote blocks so they can be easily identified&lt;/li&gt;
&lt;li&gt;To prevent markdown resetting all the counters to 1, I had to add a zero-width space between the numbers and the &lt;code&gt;.&lt;/code&gt;; otherwise all of them would have said &lt;code&gt;1.&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Title: Top 20 JavaScript One-Liners That Don't Actually Work&lt;/p&gt;

&lt;p&gt;JavaScript, with its versatility and simplicity, often lends itself to concise, one-liner solutions to common programming problems. However, not all one-liners are created equal. In this blog post, we'll explore some JavaScript one-liners that may appear to work at first glance but actually fail under closer scrutiny. Let's dive in and unravel the mysteries of these seemingly innocent lines of code.&lt;/p&gt;

&lt;p&gt;1​. &lt;strong&gt;The False Truth:&lt;/strong&gt;&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;truth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;truth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;This will never be executed&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;A common mistake of using assignment (&lt;code&gt;=&lt;/code&gt;) instead of equality (&lt;code&gt;==&lt;/code&gt; or &lt;code&gt;===&lt;/code&gt;). The condition will always be false, leading to confusion.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: Starting off strong with a two-liner. I guess the first line is just the preamble for the actual example, but I still find it funny.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;2​. &lt;strong&gt;Infinite Loop Illusion:&lt;/strong&gt;&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;for &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;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Despite the intent to count down from 0 to 9, &lt;code&gt;i&lt;/code&gt; will decrease infinitely due to &lt;code&gt;i--&lt;/code&gt;, never reaching the loop exit condition.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: There's no deeper wisdom to be found in this one, it literally just is a broken for loop. Try this with a &lt;code&gt;char&lt;/code&gt; in C and it might actually work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;3​. &lt;strong&gt;Truthy Falseness:&lt;/strong&gt;&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;if &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;This will be executed&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;An empty array is truthy in JavaScript, contrary to some expectations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: I had to double-check this one and yea, it's actually true. See? I already learned something. And within 10 minutes, I will already have forgotten it again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;4​. &lt;strong&gt;The NaN Trap:&lt;/strong&gt;&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;NaN&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;NaN&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;This will never be executed&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;Comparing NaN with anything, including itself, always returns false. Use &lt;code&gt;isNaN()&lt;/code&gt; for NaN checks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: Quality advise. But if you wanna be extra fancy, you can compare a value to itself to check if it's NaN. Your coworkers will thank you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;5​. &lt;strong&gt;Function Overload:&lt;/strong&gt;&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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;foo&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;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;bar&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;baz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;baz&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;qux&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;qux&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;func&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All functions will be called regardless of their return values. To stop on the first false value, use the logical OR (&lt;code&gt;||&lt;/code&gt;) operator.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: I tested this in the browser and looked it up on mdn. As far as I can tell, ChatGPT is straight up bullshitting us here; only the first function will be called.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;6​. &lt;strong&gt;The Lost Context:&lt;/strong&gt;&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;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;value&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="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arrow functions don't have their own &lt;code&gt;this&lt;/code&gt; context, leading to &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: This is misleading. Arrow-functions don't &lt;em&gt;set&lt;/em&gt; their own &lt;code&gt;this&lt;/code&gt; context when called, but they save the surrounding &lt;code&gt;this&lt;/code&gt;. So if this object is defined wherever a &lt;code&gt;this&lt;/code&gt; exists, it will refer to that &lt;code&gt;this&lt;/code&gt;. Sometiems this is exactly what the programmer wants. In fact, this is partly the point of arrow functions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;7​. &lt;strong&gt;The Illusion of Privacy:&lt;/strong&gt;&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;mySecrets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;getSecrets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;password&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="nx"&gt;mySecrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSecrets&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to the previous example, arrow functions inside objects don't provide privacy as expected.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: I legit have no clue what ChatGPT is even trying to tell us here. If you care about privacy, you will be excited about the sponsor of this video... Wait, wrong script.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;8​. &lt;strong&gt;Looping Headache:&lt;/strong&gt;&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;arr&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
   &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1000&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 closure captures the reference to &lt;code&gt;i&lt;/code&gt;, which is hoisted and becomes 3 by the time the callback executes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: Yes, that's what &lt;code&gt;var&lt;/code&gt; does. This is why you use &lt;code&gt;let&lt;/code&gt; instead unless you specifically want &lt;code&gt;var&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;9​. &lt;strong&gt;Promise Trap:&lt;/strong&gt;&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&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="nx"&gt;log&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="s1"&gt;bar&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;&lt;code&gt;then&lt;/code&gt; expects a function as an argument. &lt;code&gt;console.log&lt;/code&gt; gets called immediately, not when the promise resolves.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: I have no clue what the toaster is trying to tell me. Maybe it's time for the airlock.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;10​. &lt;strong&gt;String Reversal Sorrow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```javascript
const reverseString = str =&amp;gt; str.split('').reverse().join('');
console.log(reverseString('hello'));
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This approach doesn't work with Unicode characters or emoji.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: Ah yes, a true classic. Literally every "top 10 one-liners" listicle here on dev has this code, and literally every one of them has at least a million comments pointing out the unicode issue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;... (Continuing with more examples)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: Note how the title says 20, but there's actually only 10. This "AI" can't even count past 20 and people are trusting whatever it spits out without even double-checking.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In conclusion, while JavaScript one-liners can be elegant and efficient, they often hide complexities that may lead to unexpected behavior. It's crucial to thoroughly test and understand each line of code, especially when condensed into a single line. Remember, readability and clarity should never be sacrificed for brevity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Human Note&lt;/strong&gt;: And hopefully, I've been able to show that this is even more important when relying on code you get from some random AI chat.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;So there you have it. &lt;del&gt;20&lt;/del&gt; &lt;em&gt;10&lt;/em&gt; javascript one-liners that don't actually work. Except some do. I hope this silly little post has been at least somewhat amusing, and above all else, that I've been able to show why AI generated content shouldn't be trusted blindly. More importantly, I hope I've made clear how AI generated posts like this one can contain both obvious and very subtle mistakes, and posting them on the internet without even fact-checking them can seriously mess with people who are honestly trying to learn JavaScript.&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>javascript</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Callbacks and References: Allowing the Garbage to get Collected</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Wed, 07 Feb 2024 13:05:15 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/callbacks-and-references-allowing-the-garbage-to-get-collected-phb</link>
      <guid>https://forem.com/darkwiiplayer/callbacks-and-references-allowing-the-garbage-to-get-collected-phb</guid>
      <description>&lt;p&gt;JavaScript being a garbage-collected language, we don't usually have to concern ourselves with questions of allocating and releasing objects. But occasionally, specially when dealing with callbacks, it is easy to keep objects alive indefinitely, even if there are no longer any meaningful references to this.&lt;/p&gt;

&lt;p&gt;There's several tools the language provides us to deal with these situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;WeakRef&lt;/code&gt; to store a single weak reference to an object&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WeakMap&lt;/code&gt; to associate values with objects only as long as they exist&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WeakSet&lt;/code&gt; to remember objects as long as they exist&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FinalizationRegistry&lt;/code&gt; to do something when an object gets collected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on the situation, one may need one or another of these features, but the case I want to describe today will make use of the first and the last.&lt;/p&gt;

&lt;p&gt;A common case is for objects to care about certain external state changes for as long as they exist. For example, a custom element might want to listen for &lt;code&gt;"scroll"&lt;/code&gt; events on the &lt;code&gt;window&lt;/code&gt; object. But naively adding an event listener to &lt;code&gt;window&lt;/code&gt; means to keep a reference to the object. If these custom elements are short-lived but many in numbers, then they will accumulate in memory, and the additional event listeners will also pile up and waste processing power.&lt;/p&gt;

&lt;p&gt;Here's a simple example of something like this:&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;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;super&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="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&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="nf"&gt;handleScroll&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;handleScroll&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;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;top&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;scrollY&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&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;What we want is to remove the event listener by the time the object gets garbage-collected. To achieve this, we can make use of two features:&lt;/p&gt;

&lt;p&gt;Firstly, replacing the strong reference to &lt;code&gt;this&lt;/code&gt; in the event listener with a &lt;code&gt;WeakRef&lt;/code&gt; will prevent the event listener from keeping the object alive if no other references to it exist. Once the object has been collected, the &lt;code&gt;deref()&lt;/code&gt; method will just return undefined.&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;ref&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;WeakRef&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&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;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deref&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nf"&gt;handleScroll&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 will allow the object to be garbage-collected, but will keep the event listener attached, meaning it will still fire on every scroll event, fail to &lt;code&gt;deref&lt;/code&gt; the reference and therefore do nothing.&lt;/p&gt;

&lt;p&gt;An easy way to clean up event listeners is to combine &lt;code&gt;AbortController&lt;/code&gt; with &lt;code&gt;FinalizationRegistry&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The former lets us pass a signal to an event that will remove the event, while the latter allows us to run some code when certain objects get collected.&lt;/p&gt;

&lt;p&gt;The interface for this is relatively basic: We create a new &lt;code&gt;FinalizationRegistry&lt;/code&gt; and pass it a callback. Then we register an object A and an associated (different) object B. When A gets garbage-collected, it obviously can't be passed to the callback, so instead, the callback is passed B.&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;abortRegistry&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;FinalizationRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;abortRegistry&lt;/code&gt; now allows us to register an object and an associated &lt;code&gt;AbortController&lt;/code&gt;, and will call &lt;code&gt;abort()&lt;/code&gt; on the controller whenever the object gets collected.&lt;/p&gt;

&lt;p&gt;Now we just need to register our object on creation, and pass the controller's signal to the event listener.&lt;/p&gt;

&lt;p&gt;Here's the complete code:&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;abortRegistry&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;FinalizationRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;super&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;ref&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;WeakRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&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;AbortController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;abortRegistry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&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;controller&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="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scroll&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&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;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deref&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nf"&gt;handleScroll&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="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="nf"&gt;handleScroll&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;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;top&lt;/span&gt;&lt;span class="dl"&gt;"&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="nx"&gt;scrollY&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&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;h2&gt;
  
  
  Comparing to lifecycle hooks
&lt;/h2&gt;

&lt;p&gt;An easy point of criticism against the example above is that, whenever possible, resource cleanup should be achieved via the custom element API's lifecycle hooks &lt;code&gt;connectedCallback()&lt;/code&gt; and &lt;code&gt;disconnectedCallback()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two reasons why this may not be a viable alternative are that&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;This kind of cleanup might be necessary for objects that aren't DOM Elements and therefore cannot rely on their DOM connection for cleanup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There may be cases where it is necessary to continue sending signals to a custom element even while it is disconnected from the DOM, usually because it might be inserted again at a later point in time.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In either case, the only option for cleanup is keep the object set up until it actually becomes inaccessible and therefore couldn't possibly be needed anymore.&lt;/p&gt;




&lt;p&gt;And that's it 😁&lt;/p&gt;

&lt;p&gt;Did you learn something new? Was this old news to you? Let me know with a comment, and feel free to share how you handle cases like these in practice 👍&lt;/p&gt;




&lt;p&gt;Cover Image: &lt;a href="https://unsplash.com/photos/black-plastic-garbage-bin-with-wheels-beside-wall-b0p818k8Ok8" rel="noopener noreferrer"&gt;Jilbert Ebrahimi&lt;/a&gt; (Unsplash License)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>garbagecollection</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>DOM is easy: Just make it work like an Array 👍</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Fri, 12 Jan 2024 13:41:06 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/turning-the-dom-into-an-array-555g</link>
      <guid>https://forem.com/darkwiiplayer/turning-the-dom-into-an-array-555g</guid>
      <description>&lt;p&gt;Modifying DOM elements without a framework can be tedious. The method names are long, and simple common tasks are split into tiny steps.&lt;/p&gt;

&lt;p&gt;Wouldn't it be nice if the DOM was a bit more like an Array? Just push a new element, call &lt;code&gt;sort&lt;/code&gt; or even &lt;code&gt;reduce&lt;/code&gt; all the values into one.&lt;/p&gt;




&lt;p&gt;As part of a larger project, which I'm writing a longer post about at the moment, I built this convenient little helper that works well enough on its own that I want to write a separate little post about it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DarkWiiPlayer/skooma-js/blob/main/domLense.js?ts=3" rel="noopener noreferrer"&gt;&lt;code&gt;domLense&lt;/code&gt;&lt;/a&gt; is a module of less than 100 lines that lets you pretend a DOM element is actually an array. You can index it, &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;pop&lt;/code&gt; values, loop it with &lt;code&gt;forEach&lt;/code&gt; and even &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;reduce&lt;/code&gt;, etc. it to get new values.&lt;/p&gt;

&lt;p&gt;The way it works is simple: You tell the module how to get the interesting data from an element, and how to deposit different data back to it. Optionally, you define how new elements should be created.&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;transforms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;li&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;li&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="k"&gt;new&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="nb"&gt;document&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="s2"&gt;li&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;In this case, our "interesting data" is just the text content of a bunch of &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;

&lt;p&gt;All that's left now is to call &lt;code&gt;domLense&lt;/code&gt; on an unsuspecting list element, and we'll be able to treat it just like an array:&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;listElement&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;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ol&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;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;domLense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;listElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transforms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;list&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;First item on the list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;list&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Another item on the list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&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="nx"&gt;list&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;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;text&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="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&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;I've prepared a small &lt;a href="https://codepen.io/darkwiiplayer/pen/MWxbKGv" rel="noopener noreferrer"&gt;codepen demo&lt;/a&gt; to play around with this.&lt;/p&gt;

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

&lt;p&gt;The way this works isn't really that complicated. The helper simply creates a proxy that redirects integer indices to the element's child list applying the provided transformations. For any other keys, it tries looking them up in &lt;code&gt;Array.prototype&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>proxy</category>
    </item>
    <item>
      <title>Extracting a JS library from a snippets repository</title>
      <dc:creator>𒎏Wii 🏳️‍⚧️</dc:creator>
      <pubDate>Fri, 29 Sep 2023 14:36:23 +0000</pubDate>
      <link>https://forem.com/darkwiiplayer/extracting-a-js-library-from-a-snippets-repository-3m4c</link>
      <guid>https://forem.com/darkwiiplayer/extracting-a-js-library-from-a-snippets-repository-3m4c</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;A couple of years ago, I created a repository for all those JavaScript snippets and files that were were too small to turn into their own project, and could just be copy-pasted into wherever they were needed, or imported via services like jsdelivr directly through github.&lt;/p&gt;

&lt;p&gt;As some of these files grew in size and scope, this approach started to be less and less convenient, and eventually it became clear that to continue to use and maintain these bigger libraries, it would be better to just migrate them into their own repository.&lt;/p&gt;

&lt;p&gt;In principle, it would have been &lt;em&gt;good enough&lt;/em&gt; to simply copy the files into a new repo, check them in with an "Initial Commit 🎉" and refer to the collection repository where their original development history resides.&lt;/p&gt;

&lt;p&gt;But that would be boring, and I like to make things look neat, so I decided I wanted to migrate the commit history of these individual files into their new repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  How did I do it?
&lt;/h2&gt;

&lt;p&gt;The first file on my migration list is &lt;code&gt;skooma.js&lt;/code&gt;, a JS port of a Lua-library of the same name that I had gotten so used to that I wanted to use it in my front-end code as well.&lt;/p&gt;

&lt;p&gt;This file is a good case-study because it started out as &lt;code&gt;render.js&lt;/code&gt;, but got renamed to &lt;code&gt;skooma.js&lt;/code&gt; after only a few commits. This isn't too much of a problem, it just means that I also had to keep all commits including the original &lt;code&gt;render.js&lt;/code&gt; file, as well as &lt;code&gt;skooma.js&lt;/code&gt;, and its corresponding &lt;code&gt;skooma.md&lt;/code&gt; file explaining what it does.&lt;/p&gt;

&lt;p&gt;As a starting point, it was clear that the best tool for the job would be &lt;code&gt;git rebase&lt;/code&gt; in some way; &lt;em&gt;maybe&lt;/em&gt; there are other git commands to get this done with an even higher degree of automation, but for a one-off task all I needed was to automate ca. 90% of the work so I wouldn't have to cherry-pick commit by commit.&lt;/p&gt;

&lt;p&gt;Starting by just running &lt;code&gt;git rebase --root --interactive&lt;/code&gt; and having a look at the output and the available commands (I have used interactive rebase many times, but seeing the output of the to-do file before me just helped thinking about how to best handle it), I wasn't really sure if it'd be possible to really automate the entire process, so I went for the simpler and only partly automated option:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start an interactive rebase&lt;/li&gt;
&lt;li&gt;Run a script over the to-do file&lt;/li&gt;
&lt;li&gt;Run the rebase and intervene manually where needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The script
&lt;/h2&gt;

&lt;p&gt;This is the part where the magic happens. When you call &lt;code&gt;git rebase --interactive&lt;/code&gt;, you get an editor window listing everything the rebase will do, a "program" in the classic sense, which you can edit to make git do different things. The syntax looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pick 2c5a6d3 Initial commit 🎊
pick 1e6c0de Add template module to more easily write templates
pick bbbb234 Add Better HTML element class for custom elements
pick c8fd5b1 Refactor scripts into ES6 modules
pick 79af2b7 Add skooma-like functional DOM rendering helper
pick da2f082 Improve HTML render helper
pick 24ee30f Add special case for templates to render script
pick 7a61b96 Rename render to skooma.js
pick 7e72b33 Remove uppercase node special case from skooma.js
pick f97ae06 Remove setup function from template.js
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first word is the command of what will be done. &lt;code&gt;pick&lt;/code&gt; means it just uses the commit as-is. A complete list of commands can be found in a commit that git adds at the bottom of the to-do file.&lt;/p&gt;

&lt;p&gt;The second word is the commit hash, which is needed because you can move lines around in this file to re-order commits (this may of course cause conflicts that will require manual intervention, but git is smart enough to pause the rebase when that happens, give you a rough description of what's confusing it, and tell you what to do and how to continue after fixing it).&lt;/p&gt;

&lt;p&gt;Anything after that is the commit message. This only gets added to make it easier to edit the file interactively, but git itself will ignore these. Nevertheless, it would be nice to preserve these messages after modifying the to-do program to make it easier to check what is ultimately going to happen.&lt;/p&gt;

&lt;p&gt;To build my script, I decided to use Lua, for no other reason than it being the language I use for most scripting I do at home and I didn't want to bother with something exotic. I could &lt;em&gt;probably&lt;/em&gt; have done the same with a lot less code in Ruby, but since I mostly use that at work for bigger projects, I'm used to using only the kinds of features that Lua also has, and almost never have any contact with the more Perl-like features that make Ruby good for ad-hoc scripts; so ultimately, so the advantage isn't all that big in the end.&lt;/p&gt;

&lt;p&gt;Before I break this up and explain what everything does, here is the finished script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="cp"&gt;#!/usr/bin/env luajit&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;io.popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"git diff-tree --no-commit-id --name-only "&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;wanted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"render.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"skooma.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"skooma.md"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wanted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="n"&gt;wanted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;io.stdin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pick ([0-9a-f]+) (.*)$"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
         &lt;span class="nb"&gt;table.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;changed&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;wanted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
            &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
         &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pick %s %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;wanted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
               &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;[[exec if [ -f "%s" ]; then git rm %s &amp;amp;&amp;amp; git commit --amend --no-edit; fi]]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
         &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"drop %s %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ignoring the &lt;code&gt;files&lt;/code&gt; function for now, I start by defining a list of files that I want to keep. The for loop is only there to make the table function as a set as well as a list, which is a neat feature of Lua that I won't get into detail here.&lt;/p&gt;

&lt;p&gt;The script loops over all of its input lines (so I can just pipe the whole file through it from vim), and parses out a commit hash and the following commit message, discarding the &lt;code&gt;"pick"&lt;/code&gt; at the start of the line.&lt;/p&gt;

&lt;p&gt;When the line matches (which excludes empty lines and comments), it first collects a list of changed files, using the helper function from earlier. This is done using the &lt;code&gt;git diff-tree&lt;/code&gt; command (admittedly, I just googled this after spending a minute or two trying to find out how to do this from the git manpages). When given the &lt;code&gt;--no-commit-id&lt;/code&gt; and &lt;code&gt;--name-only&lt;/code&gt; flags, as well as a commit hash, it essentially just lists all the files that were changed, added, deleted, etc. in that specific commit. Exactly what I needed.&lt;/p&gt;

&lt;p&gt;After collecting the list, I loop over it again and look for each file name in the list of wanted files. If it appears, a flag is set to true, otherwise I can just &lt;code&gt;drop&lt;/code&gt; this commit. This could have been merged into the first loop, but I'm not gonna win any prizes for making a script that's gonna run 10 or 20 times in total run a few milliseconds faster.&lt;/p&gt;

&lt;p&gt;When the commit modifies one of the files I want, I add a new &lt;code&gt;pick&lt;/code&gt; command for it to keep the commit.&lt;/p&gt;

&lt;p&gt;At this point, there's a bit of a problem: Most commits &lt;em&gt;creating&lt;/em&gt; new files will usually not touch any of my relevant files. Skooma is its own thing, and I usually try to keep the git history clean. But I don't bother splitting "housekeeping" commits by file, so things like running a linter over the whole project and fixing all the warnings. This will create commits that 1. modify files I want to keep and 2. modify unwanted files which, at this point, aren't part of the repository anymore, as the commits creating them have been dropped.&lt;/p&gt;

&lt;p&gt;This is the part where git will pause the rebase, show a description of what's wrong, and ask the user to fix it. Luckily for me, and anyone who might want to do something similar, this almost always follows the same pattern: The commit says to modify a file, but the file is no longer known to git. The fix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call &lt;code&gt;git status&lt;/code&gt; to see what files are no longer there&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;git rm&lt;/code&gt; on all of the files, or just &lt;code&gt;git rm -r &amp;lt;dir&amp;gt;&lt;/code&gt; if they're all in a subdirectory&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;git rebase --continue&lt;/code&gt; to tell git all is fine now&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This leaves me with only one possible problem: If any file was, hypothetically, created in a commit that modifies one of my wanted files, then git would have no problem with that, and I'd end up with an extra file in my repo. So I added a little loop that, after each commit gets picked, loops over all the extra files, and adds an extra command for each of them which deletes them if it still exists. Since git updates the working directory as it applies commits, I only need to check for the actual file, without any git magic to see if it exists in the last applied commit. I'm fairly sure this wasn't the case in my repo, but I just wanted to add that check while I was at it to make the script a bit more robust.&lt;/p&gt;

&lt;p&gt;Here's what the resulting to-do file looks like, after feeding it through the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;drop 2c5a6d3 Initial commit 🎊
drop 1e6c0de Add template module to more easily write templates
drop bbbb234 Add Better HTML element class for custom elements
drop c8fd5b1 Refactor scripts into ES6 modules
pick 79af2b7 Add skooma-like functional DOM rendering helper
pick da2f082 Improve HTML render helper
pick 24ee30f Add special case for templates to render script
pick 7a61b96 Rename render to skooma.js
pick 7e72b33 Remove uppercase node special case from skooma.js
drop f97ae06 Remove setup function from template.js
drop d703ae1 Refactor BetterHTMLElement with more meta-magic ✨
pick 5f27242 Fix checking for template objects in skooma.js
pick c16eb29 Extend skooma to support SVG as well as HTML
pick 6f85103 Fix skooma.js syntax for strict mode
pick 60af077 Make hyphenation in consistent with browser APIs
exec if [ -f "BetterHTMLElement.js" ]; then git rm BetterHTMLElement.js &amp;amp;&amp;amp; git commit --amend --no-edit; fi
drop 137f586 Add option for customized built-in elements
pick fb6b86b Fix undefined variable in skooma.js
drop 63a85b7 Fix undeclared variable
pick 0448cd2 Update skooma.js to handle numbers
drop be31d1f Add mutation observer to BetterHTMLElement
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
      <category>scripting</category>
      <category>adhoc</category>
    </item>
  </channel>
</rss>
