/ Gists / 3 Vanilla-Lite DOM Patterns That Achieve jQuery’s Ease
On gists

3 Vanilla-Lite DOM Patterns That Achieve jQuery’s Ease

JavaScript-OOP JavaScript

1.js Raw #

// Pattern 1 — Bind Helper

export function S(selector) {
  const nodes = document.querySelectorAll(selector);
  nodes.forEach(bindAll); // Bind helpers to node(s)

  return nodes.length > 1 ? nodes : nodes[0]; // native node(s) returned
}

export function C(tag) {
  const node = document.createElement(tag);
  return bindAll(node); // Bind helpers to node(s)
}

function bindAll(node) {
  node.addClass = addClass.bind(node);
  node.attr = attr.bind(node);

  return node;
}

//--- Extension functions ---//
function addClass(...cls)   {
  this.classList.add(...cls);
  return this;
}

function attr(key, val) {
  if (val !== undefined) {
    this.setAttribute(key, val);
    return this; 
  }

  return this.getAttribute(key);
}


// Usage:
import { S, C } from './helper.js';

const btn = C('button').addClass('btn', 'primary').attr('type', 'submit');
btn.click(); // native method

S('.card').forEach(el => el.addClass('highlight').attr('data-live', '1'));

2.js Raw #

// Pattern 2 — Prototype Boost

export const S = document.querySelectorAll; // Select alias
export const C = document.createElement; // Create alias

const helpers = {
  addClass(...cls) {
    this.classList.add(...cls);
    return this; // keep it chainable
  },
  attr(key, val) {
    if (val !== undefined) {
      this.setAttribute(key, val);
      return this; // keep it chainable
    }

    return this.getAttribute(key);
  }
};

// Bind helpers to prototype
for (const [name, fn] of Object.entries(helpers)) {
  if (!(name in HTMLElement.prototype)) {
    Object.defineProperty(HTMLElement.prototype, name, {
      value: fn,
      writable: false,
      configurable: true,
      enumerable: false     // not to show in for-in loops
    });
  }
}


// usage same as 1

3.js Raw #

// Pattern 3 — Mini Wrapper (“DIY jQuery”)

class Q {
  constructor(sel) {
    if (typeof sel === 'string') {
      this.nodes = [...document.querySelectorAll(sel)];
    } else if (sel instanceof Node) {
      this.nodes = [sel];
    } else {
      this.nodes = [...sel]; // assume NodeList, HTMLCollection or Array
    }
  }

  /* shared helpers — ONE copy in memory */
  addClass(...cls) {
    this.nodes.forEach(n => n.classList.add(...cls));
    return this;
  }

  attr(key, val) {
    if (val !== undefined) {
      this.nodes.forEach(n => n.setAttribute(key, val));
      return this;
    }

    return this.nodes[0]?.getAttribute(key);
  }

  /* helpers to exit the wrapper */
  get(idx = 0) { return this.nodes[idx]; }
  all()        { return this.nodes; }
  
  
  append(child) {
    const isNode = child instanceof Node;
    const node = isNode ? child : document.createTextNode(child);
    this.appendChild(node);

    return this;
  }
}

/* shortcut that feels like jQuery */
const $ = sel => new Q(sel);
export default $;


import $ from './mini-wrapper.js';

// create 
$('<div>').addClass('card').attr('data-id', 42);

// select 
$('.item').addClass('highlight').attr('data-live', '1');
  
// grab the first matched node when you need raw DOM
const firstItem = $('.item').get(); // native HTMLElement
firstItem.focus(); // native method still works

source.txt Raw #

https://medium.com/codetodeploy/3-vanilla-lite-dom-patterns-that-achieve-jquerys-ease-1f7b92565419