YAGNI JS

This is just an idea I'm playing around with. The core is that instead of a framework or a library to have a copy/pastable piece of code you can just drop on a page and get a solid foundation for buliding interactions

  • YAGNI JS stands for: You Ain't Gonna Need It JavaScript

  • Frameworks vs libraries and how I like librarys way more

  • Including code directly on the page with modules

  • Page should be able to render anywhere

  • Module approach vs web components

  • Using query selctors and data storage

  • Basic CRUD functions

  • You could totally make this a file that gets included and that's kida inevitable, but the point is that it doesn't have to be. (And with the way I'm working with Neopoligen I just throw it in the content files for now and things work great and I can tweak as needed.)

  • NOTE: it looks like errors don't really show up right because of the way the blob is loaded if it's an inline module. TODO is to play around with that.

A Basic Idea

A YAGNI Foundation Example

function addToF(parent, tag, attrs = {}) {
  const target = getElF(parent)
  if (target) {
    const el = document.createElement(tag)
    updateAttrsF(attrs, el)
    target.appendChild(el)
    return el
  }
}

function errorF(msg) {
  console.error(`${Date.now()} - ERROR: ${msg}`)
}

function getElF(target) {
  if (typeof target === "string") {
    const el = document.querySelector(target)
    if (el) { return el } 
    else { 
      errorF(`Could not find querySelector for: ${target}`)
      return undefined 
    }
  } 
  else if (target) { 
    return target 
  } 
  else { 
    errorF(`Could not get element: ${target}`)
    return undefined 
  }
}

function getStorageF(key, defaultValue = undefined) {
  return localStorage.getItem(key) 
    ? JSON.parse(localStorage.getItem(key)).payload
    : defaultValue
}

function logF(msg) {
  console.log(`${Date.now()} - INFO: ${msg}`)
}

function setStorageF(key, value) {
  localStorage.setItem(key, JSON.stringify({ payload: value }))
}

function updateF(target, attrs = {}) {
  const el = getElF(target)
  if (el) {
    updateAttrsF(attrs, el)
  }
  return el
}

function updateAttrsF(attrs, el) {
  const nonAttrs = ['classes', 'data', 'listeners'] 
  for (let key in attrs) {
    if (!nonAttrs.includes(key)) {
      el[key] = attrs[key]
    }
  }
  for (let index in attrs.classes) {
    el.classList.add(attrs.classes[index])
  }
  for (let index in attrs.data) {
    el.dataset[attrs.data[index][0]] = attrs.data[index][1]
  }
  for (let index in attrs.listeners) {
    el.addEventListener(attrs.listeners[index][0], attrs.listeners[index][1])
  }
}
            

Basic Example

html

<div class="example-alfa"></div>
            

javascript

function initAlfaF() {
  addToF('.example-alfa', 'button', {
    innerHTML: "0",
    classes: ['green'],
    data: [ ['current', 0] ],
    listeners: [['click', (event) => {
      let value = parseInt(event.target.dataset.current, 10)
      value += 1
      event.target.dataset.current = value
      event.target.innerHTML = value
    } ]]
  })
}

document.addEventListener("DOMContentLoaded", async () => {
  initAlfaF()
})
            

More Complicated Example

Here's a more complicated example that shows interaction between elements and using local storage.

html

<div class="example-bravo"></div>
            

javascript

function initBravoF() {
  const defaultValue = 0

  addToF('.example-bravo', 'button', {
    innerHTML: "-",
    listeners: [['click', (event) => {
      setStorageF('counter', getStorageF('counter', defaultValue) - 1)
      updateF(getElF('.counter'), { 
        innerHTML: getStorageF('counter'), 
      })
    }]]
  })

  addToF('.example-bravo', 'button', {
    classes: ['counter'],
    innerHTML: getStorageF('counter', defaultValue)
  })

  addToF('.example-bravo', 'button', {
    innerHTML: "+",
    listeners: [['click', (event) => {
      setStorageF('counter', getStorageF('counter', defaultValue) + 1)
      updateF(getElF('.counter'), { 
        innerHTML: getStorageF('counter'), 
      })
    }]]
  })
}

document.addEventListener("DOMContentLoaded", async () => {
  initBravoF()
})