Skip to content

Instantly share code, notes, and snippets.

@codepo8
Last active October 30, 2020 19:08
Show Gist options
  • Save codepo8/70cbbe379cf7d148cbca0218ad22cda0 to your computer and use it in GitHub Desktop.
Save codepo8/70cbbe379cf7d148cbca0218ad22cda0 to your computer and use it in GitHub Desktop.
Resources of Chris Heilmann's talk at Halfstack Online 2

Back to basics resources

  • A long Twitter thread about how strange Facebook's HTML is with lots of arguments about generating HTML vs. readability of source code and performance
  • Playwright is a Node.js module that allows you to remotely drive Chromium, Firefox and Webkit browsers. Great for automated testing.

My pet project: CSDB Preview Bookmarklet

Checking if an image exists using fetch()

Using fetch() to check if a png image exists and load a gif if it doesn't. Using HEAD means you don't load the image, you just check that it exists.

let url = `https://csdb.dk/gfx/releases/${folderid}/${id}.png`;
fetch(url, { method: 'HEAD' })
.then(res => {
    if (res.ok) {
      out = `<img src="${url}" alt="">`;
    } else {
      url = url.replace('.png','.gif');
      out = `<img src="${url}" alt="">`;
    }
}).catch(err => console.log('Error:', err));

Parsing random HTML the lazy way

Got some HTML soup you want to work with? Use the HTML5 DOM parser in JavaScript instead of RegEx:

let parser = new DOMParser();
let parsed = parser.parseFromString(html, 'text/html');

Host your code on GitHub and execute it there, too

Hosting projects on GitHub Pages is a lot easier now than it used to be

Bookmarklets Basics

Bookmarklets are a simple way to inject content into a third party page. Here's the code to use as the link url to inject the code:

javascript:(
    function(){
        document.body.appendChild(document.createElement('script'))
            .src='https://codepo8.github.io/csdb-preview-bookmarklet/csdb-preview.js';
    }
)();

Alt text checking in the browser

Web Components super basics

A quick plain vanilla JS example of a web component <halfstack-nametag>:

Usage in HTML:

<halfstack-nametag text="Dylan" colour="green"></halfstack-nametag>

<halfstack-nametag text="Chris"></halfstack-nametag>

Usage in JavaScript:

let nametag = document.createElement('halfstack-nametag');
document.body.appendChild(nametag);
nametag.setAttribute('text', "Chris");
nametag.setAttribute('colour' ,"blue");

Full source code of the component:

class halfstacknametag extends HTMLElement {
constructor () { super() }
static get observedAttributes() { return ['text','colour'] }
get text() {return this.hasAttribute('text');}
get colour() {return this.hasAttribute('colour');}
attributeChangedCallback() {
  if(this.shadowRoot){
    let tag = this.shadowRoot.querySelector('div');
    let name = this.shadowRoot.querySelector('h2');
    name.innerHTML = this.text ?  this.getAttribute('text') : '';
    if (this.colour) { tag.style.background = this.getAttribute('colour')}
  }
}
connectedCallback () {
  let shadowRoot = this.attachShadow({mode: 'open'});
  shadowRoot.innerHTML = `
    <style>
      div {
        border-radius: 10px;font-family: impact;
        background: ${this.getAttribute('colour')||'firebrick'};
        padding: 5px; margin: 10px 0;max-width: 40vw;
      }
      h1 {color:#fff;padding:5px 10px;}
      h2 {border-radius: 10px;color: #000;background: #fff;
          margin: 5px 10px;padding :10px}
    </style>
    <div>
      <h1>Halfstack</h1><h2>${this.getAttribute('text')}</h2>
    </div>
  `;
}
}
window.customElements.define('halfstack-nametag', halfstacknametag);

Dog API and browser example

  • "The internet's biggest collection of open source dog pictures" as an API.

  • The Network Console experiment in Microsoft Edge DevTools is a straight forward way to play with APIS and to inspect any network request in more detail.

  • The Dog Browser I wrote.

  • Using a Datalist you can turn any input into an autocomplete:

      <form>
        <label for="breed">Dog breed:</label>
        <input list="allbreeds" name="breed" id="breed"></input>
        <datalist id="allbreeds">
          <!--  …  -->
        </datalist>        
      </form>

    Populating the datalist using the DOG api:

    const getbreeds = _ => {
      fetch('https://dog.ceo/api/breeds/list/all')
      .then(response => response.json())
      .then(data => {
        breeds = data.message;
        seedbreedsform();
      })
    };
    const seedbreedsform = _ => {
      let out = '';
      Object.keys(breeds).forEach(b => {
        out += `<option value="${ucfirst(b)}"/>`;
        if (breeds[b].length > 0) {
          breeds[b].forEach(s => {
            out += `<option value="${ucfirst(b)} - ${ucfirst(s)}"/>`;
          });
        }
      });
      document.querySelector('#allbreeds').innerHTML = out;
    };

    You may wonder what the ucfirst is about. Well, one drawback of datalist is that you can't style it. The Dog API returns the names of the breeds in all lowercase. When using the dependent dropdowns, you can fix that in the CSS:

    #breed, #sub, button span {
        text-transform: capitalize;
    }

    This, however doesn't work with datalist(yet), so I needed to do that in JavaScript:

    const ucfirst = str => {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }

There is also now a more detailed blog post about the dog browser on my blog.

Quickfire Round

Event Delegation

Event Delegation is a trick I've been using since around 2006. It allows you to assign one event listener to a collection of elements and check for their features to act accordingly.

Say you have the following HTML:

<ul id="dogs">
    <li><a href="#dog1">Dog1</a></li>
    <li><a href="#dog2">Dog2</a></li>
    <li><a href="#dog3">Dog3</a></li>
    <li><a href="#dog4">Dog4</a></li>
    <li><a href="#dog5">Dog5</a></li>
    <li><a href="#dog6">Dog6</a></li>
    <li><a href="#dog7">Dog7</a></li>
    <li><a href="#dog8">Dog8</a></li>
    <li><a href="#dog9">Dog9</a></li>
    <li><a href="#dog10">Dog10</a></li>
</ul>

You can use the following JavaScript to intercept all these links and code your own actions. The really useful thing here is that the content of the <ul> can change any time and you wouldn't need to re-index and re-apply your functionality. As the event is listening on the parent node all the children will report once they are available.

document.querySelector('#dogs').
  addEventListener('click', e => {
    // What was clicked?
    let t = e.target; 
    // does it have an href?
    if (t.href) { 
      console.log(t.innerText); // f.e. "Dog5"  
    }
    // if the list item was clicked
    if (t.nodeName === 'LI') { 
      // print out the link
      console.log(t.innerHTML);
    }
  e.preventDefault(); // Don't follow the links
});

Using :empty and attribute selectors to show and hide things

CSS has a lot of excellent features that allows you to avoid having to code functionality yourself. Say for example you want to have a message in between paragraphs in your document:

<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Vitae voluptate …</p>  
<div class="message"></div>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Vitae voluptate …</p>  

Normally what you'd do is to style it with all padding, margin and such and then probably add a class "hidden" to show and hide it. A simpler way may be to use the :empty selector to hide it. That way empty elements like the one above will not be shown.

.message {
    margin: 1em;
    border: 2px solid firebrick;
    padding:.5em 1em;
    color: maroon;
}
.message:empty {
  display: none;
}
/* For accessible hiding, see 
   https://zellwk.com/blog/hide-content-accessibly/
*/

You can show the message by adding a text to it and hide it by removing it, no need to do any styling or class switching in JavaScript.

You can also use attribute selectors to define different states of elements, no need to change the styles or add classes.

For example, to add a red border to all images with an empty alt attribute and to add a blinking border to all those without an alternative text, all you need is this:

<div id="images">
  <img src="https://images.dog.ceo/breeds/samoyed/n02111889_2136.jpg"
  alt="Samoyed dog">
  <img src="https://images.dog.ceo/breeds/basenji/n02110806_238.jpg"
  alt="">
  <img src="https://images.dog.ceo/breeds/basenji/n02110806_238.jpg">
  <img src="https://images.dog.ceo/breeds/samoyed/n02111889_2136.jpg"
  alt="Samoyed dog">  
</div>
img[alt=""]{
  border: 10px solid firebrick;
}
img:not([alt]) {
  border: 50px solid maroon;
  animation: fixyourimage .5s step-end infinite alternate;
}
@keyframes fixyourimage { 
    50% { border-color: firebrick; } 
}

Radio buttons as a menu using flex and <label>

By using sensible markup and flex you can turn basic HTML into something impressive. Take the following HTML:

<div id="browsers">
    <input type="radio" checked id="r1" name="browser">
    <label for="r1">Microsoft Edge</label>
    <input type="radio" id="r2" name="browser">
    <label for="r2">Firefox</label>
    <input type="radio" id="r3" name="browser">
    <label for="r3">Safari</label>
    <input type="radio" id="r4" name="browser">
    <label for="r4">Chrome</label>
    <input type="radio" id="r5" name="browser">
    <label for="r5">Brave</label>
</div>

Radio buttons sharing the same name automatically only allow the user to choose one at a time. They also get announced to a screenreader as a group. By using a label connecting the text to the checkbox, we not only make it accessible to screenreaders but we also make it easier for people to hit it - you can click on the text and the radio button gets checked.

Now onto the CSS:

#browsers {
  font-family: Arial, Helvetica, sans-serif;
  margin: 2em;
  min-height: 50px;
  /* position needed to hide the radio buttons */
  position: relative;
  /* 
    align the items with 10px gap and make them 
    use the whole space
  */ 
  display: flex;
  column-gap: 10px;
  align-items: stretch;
}
#browsers label {
  border-radius: 10px;
  text-align: center;
  flex: 1;
  line-height: 50px;
}
/* Don't show the radio buttons */
#browsers [type=radio] {
  position: absolute;
  left: -50vw;
}
/* Each label following a radion button… */
#browsers [type=radio] + label {
  background:grey;
  color: #fff;
}
/* Each label next to a checked radio button… */
#browsers [type=radio]:checked + label {
  background:darkolivegreen;
  color: #fff;
}

The often asked for "full list click example" using CSS overlays

<ul class="fullclick">
    <li>
      <h2>Heading</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam 
        blanditiis molestiae atque maxime nulla quoipsum obcaecati?</p>
      <a href="#1">Link #1</a>
    </li>
    <li>
      <h2>Heading</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magnam 
        blanditiis molestiae atque maxime nulla quoipsum obcaecati?</p>
      <a href="#2">Link #2</a>
    </li>
</ul>
.fullclick {
  font-family: Arial, Helvetica, sans-serif;
  margin: 3em 0;
  padding: 0;
}
.fullclick a {
  text-align: right;
  display: block;
}
.fullclick li {
  list-style: none;
  margin: .5em 0;
  padding: 5px;
  background: peachpuff;
  /* position needed to allow for overlay */
  position: relative;
}
/* 
  create an overlay after the link with the 
  same dimensions as the list item.
*/
.fullclick li a::after {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  content: '';
}

/* 
  If any child element of the list has focus
  change the background
*/
.fullclick li:focus-within {
  background:mediumorchid;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment