// https://medium.com/@ThinkingLoop/12-dom-tricks-no-libraries-required-61268e398c50

// 1) Query once, act many
const $ = (sel, root = document) => root.querySelector(sel);
const $$ = (sel, root = document) => [...root.querySelectorAll(sel)];

const app = $('#app');
const buttons = $$('.action', app);

buttons.forEach(btn => btn.addEventListener('click', onAction));


// 2) Event delegation (one listener, infinite items)
$('#list').addEventListener('click', e => {
  const btn = e.target.closest('[data-action="remove"]');
  if (!btn) return;
  e.preventDefault();
  btn.closest('li')?.remove();
});

// when we dont want use e.preventDeafult() -> pasive: true, better optimalization
window.addEventListener('scroll', onScroll, { passive: true });


// 3) Build with DocumentFragment (no layout thrash)
const frag = document.createDocumentFragment();
for (const item of data) {
  const li = document.createElement('li');
  li.textContent = item.name;
  frag.appendChild(li);
}
$('#list').appendChild(frag); // one commit


// 4) Render HTML safely with <template>
<template id="row-tpl">
  <li class="row">
    <span class="name"></span>
    <button data-action="remove">×</button>
  </li>
</template>

function renderRow(user) {
  const tpl = $('#row-tpl').content.cloneNode(true);
  tpl.querySelector('.name').textContent = user.name;
  return tpl;
}

const frag = document.createDocumentFragment();
users.forEach(u => frag.appendChild(renderRow(u)));
$('#list').appendChild(frag);



// 5) Class toggles beat inline styles
.card { transition: transform .2s ease; }
.card.is-active { transform: scale(1.02); }
$('.card').classList.toggle('is-active', shouldHighlight);



// 6) dataset for behavior, not style
<button data-action="copy" data-copy-text="Hello!">Copy</button>
document.addEventListener('click', e => {
  const btn = e.target.closest('[data-action="copy"]');
  if (!btn) return;
  navigator.clipboard.writeText(btn.dataset.copyText);
});


// 7) Observe, don’t poll (Intersection/Resize/Mutation)
// IntersectionObserver: lazy-load images
const io = new IntersectionObserver(entries => {
  entries.forEach(({ isIntersecting, target }) => {
    if (!isIntersecting) return;
    target.src = target.dataset.src;
    io.unobserve(target);
  });
}, { rootMargin: '200px' });

$$('img[data-src]').forEach(img => io.observe(img));

// ResizeObserver: reflow grids on size change
const ro = new ResizeObserver(entries => {
  for (const { target } of entries) layout(target);
});
ro.observe($('#grid'));


// 8) Batch mutations with requestAnimationFrame
function highlightRow(row) {
  // READ
  const { top } = row.getBoundingClientRect();

  // Schedule WRITE
  requestAnimationFrame(() => {
    row.style.setProperty('--start', `${top}px`);
    row.classList.add('highlight');
  });
}


// 9) Virtualize long lists with windowing
const viewport = $('#viewport');
const rowHeight = 32;
let start = 0, end = 0;

viewport.addEventListener('scroll', render);
window.addEventListener('resize', render);

function render() {
  const { scrollTop, clientHeight } = viewport;
  start = Math.floor(scrollTop / rowHeight);
  end = start + Math.ceil(clientHeight / rowHeight) + 5;

  const frag = document.createDocumentFragment();
  viewport.innerHTML = ''; // clear (or recycle nodes for extra perf)
  for (let i = start; i < Math.min(end, data.length); i++) {
    const div = document.createElement('div');
    div.className = 'row';
    div.style.top = `${i * rowHeight}px`;
    div.textContent = data[i].name;
    frag.appendChild(div);
  }
  viewport.appendChild(frag);
}

#viewport { position: relative; overflow: auto; height: 400px; }
.row { position: absolute; height: 32px; line-height: 32px; left: 0; right: 0; }


// 10) Custom events for clean boundaries
/ producer
function notifySaved(node, payload) {
  node.dispatchEvent(new CustomEvent('saved', { detail: payload, bubbles: true }));
}

// consumer
document.addEventListener('saved', e => {
  const { id } = e.detail;
  toast(`Saved #${id}`);
});



// 11) Progressive enhancement with feature checks
const supportsClipboard = !!navigator.clipboard?.writeText;

if (supportsClipboard) {
  document.body.classList.add('has-clipboard');
  // enable fancy copy button
} else {
  // use <input> select() + execCommand fallback or plain text
}


// 12) Micro-router: data-route + history.pushState

<nav>
  <a href="/settings" data-route>Settings</a>
  <a href="/reports"  data-route>Reports</a>
</nav>
<main id="view"></main>

const routes = {
  '/settings': () => $('#view').textContent = 'Settings ⚙️',
  '/reports':  () => $('#view').textContent = 'Reports 📈',
};

function go(path) {
  history.pushState({}, '', path);
  routes[path]?.();
}

document.addEventListener('click', e => {
  const link = e.target.closest('a[data-route]');
  if (!link) return;
  e.preventDefault();
  go(new URL(link.href).pathname);
});

window.addEventListener('popstate', () => routes[location.pathname]?.());
routes[location.pathname]?.(); // initial