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

Range Slider and Progress Bar

  1. const NUM = 8;
  2. const NUM_M_1 = NUM - 1;
  3. const ui = document.createElement('div')
  4. ui.innerHTML = `
  5.   <div class="ui">
  6.     <div class="progress" data-ref>
  7.       <div class="progressFill" data-ref></div>
  8.     </div>
  9.     <div class="text" data-ref>
  10.     0% of 100%
  11.     </div>
  12.  
  13.     <div class="dots" data-ref>
  14.     ${
  15.       `<div></div>`.repeat(NUM)
  16.     }
  17.     </div>
  18.  
  19.     <label>
  20.       drag the slider...
  21.       <input class="slider" type="range" min="0" max="100" step="1" value="0" data-ref/>
  22.     </label>
  23.   </div>
  24.   <style>
  25.     body {
  26.       font-family: sans-serif;
  27.     }
  28.     .progress, .dots {
  29.       position: relative;
  30.       width: 80%;
  31.       margin: 0 auto;
  32.       height: 30px;
  33.       border: 1px solid black;
  34.     }
  35.     .progressFill {
  36.       height: 100%;
  37.       width: 0;
  38.       background: red;
  39.     }
  40.     .text {
  41.       padding: 1em;
  42.       background: #ccc;
  43.       margin: .5em;
  44.       font-weight: bold;
  45.     }
  46.     label {
  47.       display: block;
  48.       margin: 1em;
  49.     }
  50.     .slider {
  51.       cursor: pointer;
  52.     }
  53.     .dots {
  54.       border: none;
  55.     }
  56.     .dots div {
  57.       height: 100%;
  58.       width: ${100 / NUM}%;
  59.       float: left;
  60.       transition: background 400ms ease-out;
  61.       background: transparent;
  62.       border-radius: 500px;
  63.       box-shadow: inset 0 0px 0 3px blue;
  64.     }
  65.     .dots div:nth-child(1) {
  66.       background: red;
  67.     }
  68.  
  69.   </style>
  70. `;
  71. document.body.appendChild(ui);
  72.  
  73. // select everything with a `data-ref`
  74. const els = {};
  75. ;[...document.querySelectorAll('[data-ref]')]
  76.   .forEach(el => {
  77.     els[el.classList[0]] = el;
  78.   });
  79.  
  80. function update(e) {
  81.  
  82.   // normal prog bar
  83.   const val = e.target.value;
  84.   els.progressFill.style.width = `${val}%`;
  85.   els.text.innerHTML = `
  86.     ${val}% of 100%
  87.   `;
  88.  
  89.   // segmented dot prog bar
  90.   const idx = Math.floor(val / (100 / NUM));
  91.   if (idx < NUM) { 
  92.     for (let i = 0; i < NUM; i++) {
  93.       els.dots.children[i]
  94.         .style.background = i <= idx ? 'red' : 'white'
  95.     }
  96.   }
  97. }
  98.  
  99. els.slider.addEventListener('input', update);
  100. els.slider.addEventListener('change', update);

Drag slider and watch two progress bars and a text readout update. There are some interesting vanilla tricks in this one.

This trick and a variety of variations on it is pretty powerful:

  1. const els = {};
  2. [...document.querySelectorAll('[data-ref]')].forEach(el => {
  3.   els[el.classList[0]] = el;
  4. });
// css // dom // graphics // javascript // math // strings // tricks // ui

Creative Coding Greyscale Circle Texture

  1. const canvas = document.createElement('canvas'),
  2.   c = canvas.getContext('2d');
  3.  
  4. document.body.appendChild(canvas);
  5. document.body.style.margin = 0;
  6.  
  7. function brush() {
  8.   let pnts = [];
  9.   let cx = innerWidth * Math.random(),
  10.     cy = innerHeight * Math.random();
  11.   let x = y = 0;
  12.   let ro = innerWidth / (1 + Math.random() * 2);
  13.   let oro = ro;
  14.   let r2 = innerWidth / (2 + Math.random() * 2);
  15.   let rx = innerWidth / 4;
  16.   let ry = rx;
  17.  
  18.   ro = Math.min(300, ro);
  19.  
  20.   let t = 0;
  21.   let vt = 0.05;
  22.   let wt = 0.05;
  23.   let fric = 0.99;
  24.   let t2 = 0;
  25.   let x2 = 0;
  26.   let y2 = 0;
  27.   let vx2 = 0;
  28.   let vy2 = 0;
  29.   let col = ['black', 'white'][Math.floor(Math.random() * 2)];
  30.   let cc = 0; //[0, 255][Math.floor(Math.random() * 2)];
  31.   let s = 0.05 + Math.random() * 0.25;
  32.  
  33.   function draw() {
  34.     c.save();
  35.     c.translate(cx, cy);
  36.     c.scale(s, s);
  37.     for (let i = 0; i < 50; i++) {
  38.       if (Math.random() < 0.13) {
  39.         vt += Math.random() * 0.1 - 0.05;
  40.       }
  41.       if (Math.random() < 0.13) {
  42.         wt += Math.random() * 0.1 - 0.05;
  43.       }
  44.       vt *= 0.99;
  45.       wt *= 0.98;
  46.       rx += (vt + wt * r2 + ro - rx) / 12;
  47.       ry += (vt + wt * r2 + ro - ry) / 12;
  48.  
  49.       if (Math.random() < 0.001) {
  50.         ro += 100 * Math.sin(t2 / 5);
  51.         if (Math.abs(ro) < 50) ro = 50;
  52.       }
  53.       t2 += 0.01;
  54.       x = rx * Math.cos(t / 10) + y2;
  55.       y = ry * Math.sin(t / 10) + x2;
  56.  
  57.       if (Math.random() < 0.01) {
  58.         vx2 += Math.random() * 0.2 - 0.1;
  59.       }
  60.       if (Math.random() < 0.01) {
  61.         vy2 += Math.random() * 0.2 - 0.1;
  62.       }
  63.  
  64.       vx2 *= fric;
  65.       vy2 *= fric;
  66.  
  67.       x2 += vx2;
  68.       y2 += vy2;
  69.       t += vt;
  70.  
  71.       c.fillStyle = col;
  72.       c.fillRect(x, y, 4, 4);
  73.       pnts.push([x, y]);
  74.  
  75.       if (Math.random() < 0.1) {
  76.         const a = pnts[Math.floor(Math.random() * pnts.length)];
  77.         const b = pnts[Math.floor(Math.random() * pnts.length)];
  78.         c.strokeStyle = `rgba(${cc}, ${cc}, ${cc}, ${Math.random() / 2})`;
  79.         c.beginPath();
  80.         c.moveTo(a[0], a[1]);
  81.         c.lineTo(b[0], b[1]);
  82.         c.stroke();
  83.       }
  84.     }
  85.     c.restore();
  86.   }
  87.   return draw;
  88. }
  89.  
  90. const NUM = 20;
  91. let brushes = [];
  92.  
  93. function resize() {
  94.   canvas.width = innerWidth;
  95.   canvas.height = innerHeight;
  96.   c.fillStyle = 'gray';
  97.   c.fillRect(0, 0, canvas.width, canvas.height);
  98.  
  99.   brushes = [];
  100.   for (let i = 0; i < NUM; i++) {
  101.     brushes.push(brush());
  102.   }
  103. }
  104. addEventListener('resize', resize);
  105. resize();
  106.  
  107. function loop() {
  108.   c.globalAlpha = 1;
  109.   c.fillStyle = 'rgba(155, 155, 155, 0.0018)';
  110.   c.fillRect(0, 0, canvas.width, canvas.height);
  111.   c.globalAlpha = 0.5;
  112.   for (let i = 0; i < brushes.length; i++) {
  113.     brushes[i]();
  114.   }
  115.   requestAnimationFrame(loop);
  116. }
  117. loop();

I learned how to code making things like this in Director Lingo. This speed coded snippet draws some shaky circles and lines. (Make sure to view it in fullscreen).

Set Multiple Attributes (hack)

  1. // hacky way to set many attributes - just for fun
  2. const el = document.body.appendChild(
  3.   document.createElement('button'));
  4. let i, a;
  5. el.innerHTML = 'hello';
  6. for (i in a = {
  7.   id: 'xyz',
  8.   title: 'hi there...',
  9.   style: `
  10.     background: red; 
  11.     color:white;
  12.     font-size: 1.4em;
  13.   `
  14. }) el.setAttribute(i, a[i]);

Chances are you don’t want to use this unless you are just speed coding, prototyping or playing around

If you do want to set multiple attributes, you’ll likely be doing so in an abstract way with one of the many popular js libs or frameworks.

In my personal projects I usually don’t use DOM related libs or frameworks, so I do this or something like it:

  1. function attrs(el, attrs) {
  2.   for (let i in attrs) {
  3.     el.setAttribute(i, attrs[i])
  4.   }
  5. }

I’ll always miss jQuery, used it for many years and found it extremely intuitive and a huge time saver… The above attrs function is just a crappier version of the jQuery attr method. Read more about that here in the docs.

// dom // hacks // javascript

Rectangle Intersection

  1. function testRectIntersection(divA, divB) {
  2.   const rectA = divA.getBoundingClientRect();
  3.   const rectB = divB.getBoundingClientRect();
  4.  
  5.   return (
  6.     rectA.left < rectB.right &&
  7.     rectA.right > rectB.left &&
  8.     rectA.top < rectB.bottom &&
  9.     rectA.bottom > rectB.top
  10.   );
  11. }
  12.  
  13. function rect(color, x, y, width = 100, height = 100) {
  14.   const el = document.body.appendChild(document.createElement('div'));
  15.   Object.assign(el.style, {
  16.     position: 'absolute',
  17.     left: `${x}px`,
  18.     top: `${y}px`,
  19.     width: `${width}px`,
  20.     height: `${height}px`,
  21.     background: color
  22.   });
  23.  
  24.   return el;
  25. }
  26.  
  27. const redBox = rect('red', 20, 20, 100, 100);
  28. const mover = rect('green', 130, 20, 100, 100);
  29. // with a `mousemove` only this won't be _great_ on mobile
  30. document.addEventListener('mousemove', e => {
  31.   Object.assign(mover.style, {
  32.     left: `${e.clientX - parseFloat(mover.style.width) / 2}px`,
  33.     top: `${e.clientY - parseFloat(mover.style.height) / 2}px`
  34.   });
  35.  
  36.   if (testRectIntersection(mover, redBox)) {
  37.     redBox.style.background = 'blue';
  38.   } else {
  39.     redBox.style.background = 'red';
  40.   }
  41. });

Move your mouse so that the green rectangle touches the red.

This snippet shows how to test the if two non-rotated rectangles are intersecting. The only real part of the code that does this is:

  1. rectA.left < rectB.right &&
  2. rectA.right > rectB.left &&
  3. rectA.top < rectB.bottom &&
  4. rectA.bottom > rectB.top

With an understanding of how x/y coordinates work on the web, it can be fun to draw this out and figure out why it works on your own.

I’ve used getBoundingClientRect to obtain the rectangles of two divs here. In the rare case where you need to calculate many many intersections frequently, getBoundingClientRect should be avoided if possible as it can get slow for a variety of reasons. The alternative to getBoundingClientRect is to keep track of all the rectangle coordinate and size information yourself.

// css // dom // graphics // javascript // math // tricks

Replace with “O”

  1. const txt = document.body.appendChild(
  2.   document.createElement('textarea')
  3. );
  4.  
  5. document.body.style.margin = 0;
  6. Object.assign(txt.style, {
  7.   position: 'relative',
  8.   width: '90%',
  9.   fontSize: '1.5em',
  10.   borderRadius: 0,
  11.   padding: '1em',
  12.   outline: 'none'
  13. });
  14. txt.setAttribute('placeholder', 'enter some text');
  15.  
  16. const result = document.body.appendChild(
  17.   document.createElement('div')
  18. );
  19.  
  20. Object.assign(result.style, {
  21.   padding: '1em', 
  22.   fontSize: '2em'
  23. });
  24.  
  25. txt.addEventListener('input', () => { 
  26.   result.innerHTML = txt.value.replace(/[aeiou]/gi, 'o');
  27. });

This snippet takes input into a textfield and replaces all vowels with the letter “o”. I saw a meme that did something like this with musical instruments piano = poono, guitar = gootor etc..

// css // dom // regex // strings
snippet.zone ~ 2021-22 /// {s/z}