Rules specific to DOM manipulation and browser environment patterns for web development.
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;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;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);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');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;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' });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)');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',
},
},
];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',
},
},
];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
}],
},
},
];