or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

anti-pattern-prevention-rules.mdarray-object-rules.mdcode-quality-rules.mdcode-style-rules.mddom-browser-rules.mdimport-export-rules.mdindex.mdmodern-javascript-rules.mdplugin-configuration.md
tile.json

dom-browser-rules.mddocs/

DOM and Browser Rules

Rules specific to DOM manipulation and browser environment patterns for web development.

Capabilities

DOM Node Manipulation Rules

Rules for modern DOM node manipulation methods.

/**
 * Prefers Node.append() over Node.appendChild()
 */
'unicorn/prefer-dom-node-append': 'error' | 'warn' | 'off';

/**
 * Prefers Node.remove() over parentNode.removeChild()
 */
'unicorn/prefer-dom-node-remove': 'error' | 'warn' | 'off';

/**
 * Prefers Node.textContent over Node.innerText
 */
'unicorn/prefer-dom-node-text-content': 'error' | 'warn' | 'off';

/**
 * Prefers dataset API over getAttribute/setAttribute for data attributes
 */
'unicorn/prefer-dom-node-dataset': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - legacy DOM manipulation
const parent = document.getElementById('container');
const child = document.createElement('div');
parent.appendChild(child);

// ✅ Good - modern DOM manipulation
const parent = document.getElementById('container');
const child = document.createElement('div');
parent.append(child);

// ❌ Bad - legacy node removal
const element = document.getElementById('target');
element.parentNode.removeChild(element);

// ✅ Good - modern node removal
const element = document.getElementById('target');
element.remove();

// ❌ Bad - using innerText
element.innerText = 'Hello World';

// ✅ Good - using textContent
element.textContent = 'Hello World';

// ❌ Bad - manual data attribute handling
element.setAttribute('data-user-id', '123');
const userId = element.getAttribute('data-user-id');

// ✅ Good - using dataset API
element.dataset.userId = '123';
const userId = element.dataset.userId;

Element Selection Rules

Rules for modern element selection methods.

/**
 * Prefers querySelector/querySelectorAll over legacy methods
 */
'unicorn/prefer-query-selector': 'error' | 'warn' | 'off' | [
  'error' | 'warn',
  {
    checkId?: boolean; // Default: true
    checkClass?: boolean; // Default: true
    checkTag?: boolean; // Default: false
    checkAttribute?: boolean; // Default: true
  }
];

/**
 * Prefers modern DOM APIs over legacy methods
 */
'unicorn/prefer-modern-dom-apis': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - legacy element selection
const elementById = document.getElementById('myId');
const elementsByClass = document.getElementsByClassName('myClass');
const elementsByTag = document.getElementsByTagName('div');

// ✅ Good - modern element selection
const elementById = document.querySelector('#myId');
const elementsByClass = document.querySelectorAll('.myClass');
const elementsByTag = document.querySelectorAll('div');

// ❌ Bad - legacy DOM methods
element.setAttribute('hidden', '');
const isHidden = element.hasAttribute('hidden');

// ✅ Good - modern DOM properties
element.hidden = true;
const isHidden = element.hidden;

Event Handling Rules

Rules for modern event handling patterns.

/**
 * Prefers addEventListener over on* properties
 */
'unicorn/prefer-add-event-listener': 'error' | 'warn' | 'off' | [
  'error' | 'warn',
  {
    excludedPackages?: string[]; // Default: ['koa', 'sax']
  }
];

/**
 * Prefers EventTarget over other event handling patterns
 */
'unicorn/prefer-event-target': 'error' | 'warn' | 'off';

/**
 * Prefers KeyboardEvent.key over KeyboardEvent.keyCode
 */
'unicorn/prefer-keyboard-event-key': 'error' | 'warn' | 'off';

/**
 * Prevents invalid removeEventListener calls
 */
'unicorn/no-invalid-remove-event-listener': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - on* event properties
button.onclick = handleClick;
window.onload = initialize;

// ✅ Good - addEventListener
button.addEventListener('click', handleClick);
window.addEventListener('load', initialize);

// ❌ Bad - legacy keyboard event handling
document.addEventListener('keydown', (event) => {
  if (event.keyCode === 13) { // Enter key
    handleEnter();
  }
  if (event.keyCode === 27) { // Escape key
    handleEscape();
  }
});

// ✅ Good - modern keyboard event handling
document.addEventListener('keydown', (event) => {
  if (event.key === 'Enter') {
    handleEnter();
  }
  if (event.key === 'Escape') {
    handleEscape();
  }
});

// ❌ Bad - invalid removeEventListener
element.addEventListener('click', () => handleClick());
element.removeEventListener('click', () => handleClick()); // Won't work

// ✅ Good - proper removeEventListener
const clickHandler = () => handleClick();
element.addEventListener('click', clickHandler);
element.removeEventListener('click', clickHandler);

Browser API Rules

Rules for modern browser APIs and patterns.

/**
 * Prevents document.cookie usage in favor of proper cookie handling
 */
'unicorn/no-document-cookie': 'error' | 'warn' | 'off';

/**
 * Prefers Blob reading methods over alternatives
 */
'unicorn/prefer-blob-reading-methods': 'error' | 'warn' | 'off';

/**
 * Prevents invalid fetch options
 */
'unicorn/no-invalid-fetch-options': 'error' | 'warn' | 'off';

/**
 * Requires postMessage target origin specification
 */
'unicorn/require-post-message-target-origin': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - direct document.cookie manipulation
const getCookie = (name) => {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
};

// ✅ Good - proper cookie handling with library or modern API
// Use a cookie library or the newer Cookie Store API when available
if ('cookieStore' in window) {
  const cookie = await cookieStore.get(name);
}

// ❌ Bad - manual blob reading
const reader = new FileReader();
reader.onload = (event) => {
  const text = event.target.result;
};
reader.readAsText(blob);

// ✅ Good - modern blob reading
const text = await blob.text();
const arrayBuffer = await blob.arrayBuffer();
const dataUrl = await blob.dataURL();

// ❌ Bad - invalid fetch options
fetch('/api/data', {
  method: 'GET',
  body: JSON.stringify(data), // Invalid: GET requests can't have body
});

// ✅ Good - valid fetch options
fetch('/api/data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(data),
});

// ❌ Bad - postMessage without target origin
window.postMessage(data, '*');

// ✅ Good - postMessage with specific target origin
window.postMessage(data, 'https://trusted-domain.com');

Form and Input Handling Rules

Rules for modern form and input handling patterns.

/**
 * Modern DOM APIs include better form handling patterns
 */
'unicorn/prefer-modern-dom-apis': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - legacy form data handling
const form = document.forms.myForm;
const data = {};
for (const element of form.elements) {
  if (element.name) {
    data[element.name] = element.value;
  }
}

// ✅ Good - modern FormData API
const form = document.forms.myForm;
const formData = new FormData(form);
const data = Object.fromEntries(formData);

// ❌ Bad - manual input validation
const email = input.value;
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);

// ✅ Good - built-in constraint validation
const isValid = input.checkValidity();
const validationMessage = input.validationMessage;

Custom Elements and Web Components

Rules for custom elements and web components.

/**
 * EventTarget patterns for custom events
 */
'unicorn/prefer-event-target': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - manual event handling for custom elements
class MyElement extends HTMLElement {
  constructor() {
    super();
    this.listeners = new Map();
  }
  
  on(event, handler) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event).push(handler);
  }
  
  emit(event, data) {
    if (this.listeners.has(event)) {
      this.listeners.get(event).forEach(handler => handler(data));
    }
  }
}

// ✅ Good - using EventTarget for custom elements
class MyElement extends HTMLElement {
  constructor() {
    super();
  }
  
  emitCustomEvent(type, detail) {
    this.dispatchEvent(new CustomEvent(type, { detail }));
  }
}

// Usage
const element = new MyElement();
element.addEventListener('custom-event', (event) => {
  console.log(event.detail);
});
element.emitCustomEvent('custom-event', { data: 'value' });

Performance and Optimization Rules

Rules for DOM performance optimization.

/**
 * Query selector optimization patterns
 */
'unicorn/prefer-query-selector': [
  'error',
  {
    checkId: true,    // querySelector('#id') over getElementById
    checkClass: true, // querySelectorAll('.class') over getElementsByClassName
    checkTag: false,  // Allow getElementsByTagName for performance
  }
];

Usage Examples:

// Performance considerations:

// ❌ Less efficient for simple ID lookups (sometimes)
const element = document.querySelector('#myId');

// ✅ More efficient for simple ID lookups (sometimes)
const element = document.getElementById('myId');

// But querySelector is more consistent and flexible:
// ✅ Consistent API for complex selectors
const elements = document.querySelectorAll('.class[data-active="true"]');

// ✅ Better for chaining and complex queries
const specificElement = container.querySelector('.item:nth-child(3)');

Configuration Examples

Comprehensive DOM and Browser Rules

export default [
  {
    plugins: {
      unicorn: eslintPluginUnicorn,
    },
    rules: {
      // DOM manipulation
      'unicorn/prefer-dom-node-append': 'error',
      'unicorn/prefer-dom-node-remove': 'error',
      'unicorn/prefer-dom-node-text-content': 'error',
      'unicorn/prefer-dom-node-dataset': 'error',
      
      // Element selection
      'unicorn/prefer-query-selector': ['error', {
        checkId: true,
        checkClass: true,
        checkTag: false, // Keep for performance
        checkAttribute: true,
      }],
      'unicorn/prefer-modern-dom-apis': 'error',
      
      // Event handling
      'unicorn/prefer-add-event-listener': 'error',
      'unicorn/prefer-event-target': 'error',
      'unicorn/prefer-keyboard-event-key': 'error',
      'unicorn/no-invalid-remove-event-listener': 'error',
      
      // Browser APIs
      'unicorn/no-document-cookie': 'error',
      'unicorn/prefer-blob-reading-methods': 'error',
      'unicorn/no-invalid-fetch-options': 'error',
      'unicorn/require-post-message-target-origin': 'error',
    },
  },
];

React/SPA-Friendly Configuration

export default [
  {
    plugins: {
      unicorn: eslintPluginUnicorn,
    },
    rules: {
      // DOM manipulation (less relevant in React)
      'unicorn/prefer-dom-node-append': 'warn', // React handles this
      'unicorn/prefer-dom-node-remove': 'warn', // React handles this
      'unicorn/prefer-dom-node-text-content': 'error',
      'unicorn/prefer-dom-node-dataset': 'error',
      
      // Event handling (important for React)
      'unicorn/prefer-add-event-listener': ['error', {
        excludedPackages: ['react', '@types/react']
      }],
      'unicorn/prefer-keyboard-event-key': 'error',
      'unicorn/no-invalid-remove-event-listener': 'error',
      
      // Browser APIs (still relevant)
      'unicorn/no-document-cookie': 'error',
      'unicorn/prefer-blob-reading-methods': 'error',
      'unicorn/no-invalid-fetch-options': 'error',
      'unicorn/require-post-message-target-origin': 'error',
    },
  },
];

Legacy Browser Support Configuration

export default [
  {
    plugins: {
      unicorn: eslintPluginUnicorn,
    },
    rules: {
      // Be more lenient with modern APIs for legacy support
      'unicorn/prefer-modern-dom-apis': 'warn',
      'unicorn/prefer-blob-reading-methods': 'off', // May not be supported
      
      // Still enforce good practices
      'unicorn/prefer-add-event-listener': 'error',
      'unicorn/prefer-keyboard-event-key': 'error',
      'unicorn/no-invalid-fetch-options': 'error',
      'unicorn/no-document-cookie': 'warn', // May be necessary fallback
      
      // Query selector support varies
      'unicorn/prefer-query-selector': ['warn', {
        checkId: false,    // getElementById has better support
        checkClass: false, // getElementsByClassName has better support  
        checkTag: false,   // getElementsByTagName has better support
      }],
    },
  },
];