)
}
}
)
(
}
{
)
)
(
)
(
(
{
}
)
(
)
}
)
)
{
(
(
)
)
}
)
(
}

Check if HTML Tag is Valid

  1. const isTag = tag => { 
  2.   return !/Unknown/.test(document.createElement(tag) + '')
  3. }
  4.  
  5. console.log('section:', isTag('section'))
  6. console.log('div:', isTag('div'))
  7. console.log('nav:', isTag('nav'))
  8. console.log('banana:', isTag('banana'))

Check if a tagName is a valid html element.

When casting a dom node to a string, you’ll get a class name like this:

  1. document.createElement('div') + ''
  2. // '[object HTMLDivElement]'
  3.  
  4. // you can cast to a string with `toString` if 
  5. // you want things to be more readable
  6. document.createElement('section').toString()
  7. // '[object HTMLElement]'
  8.  
  9. document.createElement('input') + ''
  10. // '[object HTMLInputElement]'

When you try to create something with an unknown tagName you’ll end up with:

  1. document.createElement('banana') + ''
  2. // '[object HTMLUnknownElement]'

So, testing for the presence of the string Unknown is an easy way to check if a tagName is valid in a given browser. This is the perfect kind of thing to memoize:

  1. const tags = {}
  2. const isTag = tag => { 
  3.   if (tags[tag] != null) {
  4.     // already calculated
  5.     console.log('loking up: ', tag, tags[tag]);
  6.     return tags[tag]
  7.   }
  8.   const result = !/Unknown/.test(document.createElement(tag) + '')
  9.   tags[tag] = result
  10.   return result
  11. }
  12.  
  13. console.log('calculator', isTag('calculator'))
  14. console.log('calculator', isTag('calculator'))
  15.  
  16. console.log('strong', isTag('strong'))
  17. console.log('strong', isTag('strong'))
// dom // html // javascript // regex // strings // tricks // ui

Tab Timer

  1. <title id=t></title>
  2. <script>
  3.   st = Date.now()
  4.   setInterval(_ => {
  5.     s = Date.now() - st
  6.     t.innerHTML = 
  7.       ~~(s/6e4)+':'+(~~(s/1e3)%60)
  8.   }, 500)
  9. </script>

I speed coded this recently to put a timer in a brower tab.

Have a look at it here in a new tab…

Obviously this is pretty odd – but it does work for my purposes 😉

Wobbling Discord Blob

  1. const SCALE = 0.25;
  2. const TWO_PI = Math.PI * 2;
  3. const HALF_PI = Math.PI / 2;
  4. const canvas = document.createElement("canvas");
  5. const c = canvas.getContext("2d");
  6.  
  7. canvas.width = window.innerWidth;
  8. canvas.height = window.innerHeight;
  9. document.body.appendChild(canvas);
  10.  
  11. class Blob {
  12.   constructor() {
  13.     this.wobbleIncrement = 0;
  14.     // use this to change the size of the blob
  15.     // use this to change the size of the blob
  16.     this.radius = 1100;
  17.     // think of this as detail level
  18.     // number of conections in the `bezierSkin`
  19.     this.segments = 14;
  20.     this.step = HALF_PI / this.segments;
  21.     this.anchors = [];
  22.     this.radii = [];
  23.     this.thetaOff = [];
  24.  
  25.     const bumpRadius = 200;
  26.     const halfBumpRadius = bumpRadius / 2;
  27.  
  28.     for (let i = 0; i < this.segments + 2; i++) {
  29.       this.anchors.push(0, 0);
  30.       this.radii.push(Math.random() * bumpRadius - halfBumpRadius);
  31.       this.thetaOff.push(Math.random() * TWO_PI);
  32.     }
  33.  
  34.     this.theta = 0;
  35.     this.thetaRamp = 0;
  36.     this.thetaRampDest = 12;
  37.     this.rampDamp = 25;
  38.   }
  39.   update() {
  40.     this.thetaRamp += (this.thetaRampDest - this.thetaRamp) / this.rampDamp;
  41.     this.theta += 0.03;
  42.  
  43.     this.anchors = [0, this.radius];
  44.     for (let i = 0; i <= this.segments + 2; i++) {
  45.       const sine = Math.sin(this.thetaOff[i] + this.theta + this.thetaRamp);
  46.       const rad = this.radius + this.radii[i] * sine;
  47.       const theta = this.step * i;
  48.       const x = rad * Math.sin(theta);
  49.       const y = rad * Math.cos(theta);
  50.       this.anchors.push(x, y);
  51.     }
  52.  
  53.     c.save();
  54.     c.translate(-10, -10);
  55.     c.scale(SCALE, SCALE);
  56.     c.fillStyle = "blue";
  57.     c.beginPath();
  58.     c.moveTo(0, 0);
  59.     bezierSkin(this.anchors, false);
  60.     c.lineTo(0, 0);
  61.     c.fill();
  62.     c.restore();
  63.   }
  64. }
  65.  
  66. const blob = new Blob();
  67.  
  68. function loop() {
  69.   c.clearRect(0, 0, canvas.width, canvas.height);
  70.   blob.update();
  71.   window.requestAnimationFrame(loop);
  72. }
  73. loop();
  74.  
  75. // array of xy coords, closed boolean
  76. function bezierSkin(bez, closed = true) {
  77.   const avg = calcAvgs(bez);
  78.   const leng = bez.length;
  79.  
  80.   if (closed) {
  81.     c.moveTo(avg[0], avg[1]);
  82.     for (let i = 2; i < leng; i += 2) {
  83.       let n = i + 1;
  84.       c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  85.     }
  86.     c.quadraticCurveTo(bez[0], bez[1], avg[0], avg[1]);
  87.   } else {
  88.     c.moveTo(bez[0], bez[1]);
  89.     c.lineTo(avg[0], avg[1]);
  90.     for (let i = 2; i < leng - 2; i += 2) {
  91.       let n = i + 1;
  92.       c.quadraticCurveTo(bez[i], bez[n], avg[i], avg[n]);
  93.     }
  94.     c.lineTo(bez[leng - 2], bez[leng - 1]);
  95.   }
  96. }
  97.  
  98. // create anchor points by averaging the control points
  99. function calcAvgs(p) {
  100.   const avg = [];
  101.   const leng = p.length;
  102.   let prev;
  103.  
  104.   for (let i = 2; i < leng; i++) {
  105.     prev = i - 2;
  106.     avg.push((p[prev] + p[i]) / 2);
  107.   }
  108.   // close
  109.   avg.push((p[0] + p[leng - 2]) / 2, (p[1] + p[leng - 1]) / 2);
  110.   return avg;
  111. }

This is a stackoverflow answer of mine. The question was asking how to create a wobbling blob like the one in the background of the discord login page. Take a look at the answer here.

User AmooHesam wrapped it up in a github repo here.

// animation // canvas // javascript // math // paths // ui

elementUnderPoint and elementsUnderPoint

  1. document.addEventListener('mousemove', e => {
  2.   const el = document.elementFromPoint(e.clientX, e.clientY);
  3.  
  4.   // msElementsFromPoint for old IE versions (which will thankfully be gone soon)
  5.   const els = document.elementsFromPoint(e.clientX, e.clientY);
  6.   if (el != null) { 
  7.     console.log(
  8.       el.className, 
  9.       els
  10.     )
  11.  
  12.     el.style.border = '1px solid white';
  13.   }
  14. });
  15.  
  16. // prevent scrolling
  17. document.addEventListener('touchmove', e => e.preventDefault(), { passive: false });
  18.  
  19. // hardcoded for clarity
  20. document.addEventListener('touchmove', e => {
  21.   const x = e.touches[0].clientX;
  22.   const y = e.touches[0].clientY;
  23.   const el = document.elementFromPoint(x, y);
  24.   const els = document.elementsFromPoint(x, y);
  25.  
  26.   if (el != null) { 
  27.     console.log(
  28.       el.className,
  29.       els
  30.     )
  31.     el.style.border = '1px solid white';
  32.   }
  33. });
  34.  
  35. Object.assign(document.body.style, {
  36.   background: '#000', 
  37.   margin: 0
  38. });
  39.  
  40. // put some boxes on the page
  41. for (let i = 0; i < 200; i++) { 
  42.   const size = 10 + Math.random() * 80;
  43.   const halfSize = size / 2;
  44.   const x = Math.random() * 120 - 10;
  45.   const y = Math.random() * 120 - 10;
  46.   const rot = x * 3 + Math.random() * 90 - 45;
  47.   const col = `hsl(${rot}, 50%, 50%)`;
  48.   document.body.innerHTML += `
  49.     <div class="box box-${i}" 
  50.       style="
  51.         transform: rotate(${rot}deg);
  52.         position: absolute;
  53.         background: ${col};
  54.         width: ${size}px; 
  55.         height: ${size}px;
  56.         left: ${x}%;
  57.         top: ${y}%;
  58.       "></div>`;
  59. }

Obtain the element or elements underneath the mouse or touch point. Open your dev console to see the results.

// dom // javascript // ui

Bad/Simple UI Counter

  1. <script>
  2.   count = 0
  3. </script>
  4. <div>
  5.   <p>You clicked <span id="countEl">0</span> times</p>
  6.   <button onclick="countEl.innerHTML = ++count">Click me</button>
  7. </div>

This kind of thing is often showcased on UI Framework demo pages. I thought it would be interesting to create it in the simplest way possible while still being readable. This is the only code needed for the entire web page… the browser will figure out the rest… 😀

// javascript // tricks // ui
snippet.zone ~ 2021-24 /// {s/z}