iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Strip HTML (don't parse it)

Stripping HTML with regex (and when not to): the trade-offs, the safer paths, and the patterns for quick text cleanup.

Regex — HTML strip (defensively)

EXAMPLE
// ===== The famous warning =====
// 'You can't parse HTML with regex'  — true for full parsing.
// HTML is not a regular language; nested tags, attributes, comments, scripts break naive regexes.

// ===== When regex is OK =====
// - Quick + dirty cleanup of trusted markup
// - Logging / display where small errors are tolerable
// - Pre-processing before a real sanitiser

// When regex is NOT OK:
// - Sanitising untrusted input for XSS protection
// - Extracting structured data from arbitrary HTML
// - Anywhere correctness matters

// For untrusted input always use a real sanitiser (DOMPurify, sanitize-html, bleach, HTMLPurifier).

// ===== Naive strip =====
const stripped = html.replace(/<[^>]*>/g, '');
// Removes simple tags. Misses:
// - Scripts (still leaves the content)
// - Comments
// - Entities (&lt; etc — leaves as text)
// - Nested < > inside attributes (rare but exists)

// ===== Slightly better =====
function stripHtmlBasic(s) {
  return s
    .replace(/<script[\s\S]*?<\/script>/gi, '')    // drop script + contents
    .replace(/<style[\s\S]*?<\/style>/gi, '')      // drop style + contents
    .replace(/<!--[\s\S]*?-->/g, '')                // drop comments
    .replace(/<[^>]+>/g, ' ')                          // strip remaining tags
    .replace(/&nbsp;/g, ' ').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&#39;/g, "'")
    .replace(/\s+/g, ' ').trim();
}

// Still imperfect: unknown entities, malformed tags, CDATA. For real cleanup -> use a parser.

// ===== Safer: use DOMParser (browser) =====
function stripHtmlSafe(s) {
  const doc = new DOMParser().parseFromString(s, 'text/html');
  // Drop scripts / styles in case of edge cases:
  doc.querySelectorAll('script, style').forEach(el => el.remove());
  return doc.body.textContent || '';
}

// ===== Node: use a parser =====
// npm install node-html-parser
import { parse } from 'node-html-parser';
function stripHtmlNode(s) {
  return parse(s).textContent;
}

// Or jsdom for a more complete DOM:
import { JSDOM } from 'jsdom';
const dom = new JSDOM(s);
const text = dom.window.document.body.textContent;

// ===== Sanitiser (for keeping SOME markup) =====
// npm install sanitize-html
import sanitizeHtml from 'sanitize-html';
const clean = sanitizeHtml(s, {
  allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'ul', 'ol', 'li', 'br'],
  allowedAttributes: { a: ['href', 'title'] },
  allowedSchemes: ['http', 'https', 'mailto'],
});

// ===== Common quick-cleanup recipes =====
// Strip tabs + multiple spaces:
text.replace(/\s+/g, ' ').trim();

// Remove HTML comments only:
html.replace(/<!--[\s\S]*?-->/g, '');

// Replace <br> with newlines before stripping:
html.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]+>/g, '');

// Extract text inside a single tag:
const m = html.match(/<h1[^>]*>(.*?)<\/h1>/s);
const title = m?.[1].replace(/<[^>]+>/g, '').trim();

// ===== Patterns to internalise =====
// - For UNTRUSTED input -> sanitiser library
// - For TRUSTED quick cleanup -> regex is fine, but layered
// - For STRUCTURED extraction -> DOMParser / jsdom / cheerio
// - Always unescape entities after stripping tags

// ===== Pitfalls =====
// - Naive <[^>]*> misses scripts / styles / comments
// - Entity-encoded < > survive a naive strip
// - Attribute values with > can confuse [^>]*
// - Trusting 'just regex' for XSS prevention -> WHY YOU SHOULD NOT

Why it matters

Regex HTML strip is fine for trusted, low-stakes cleanup. For untrusted input, use a real sanitiser (DOMPurify, sanitize-html) and for structured extraction, use a parser (DOMParser, cheerio, jsdom). The naive <[^>]*> recipe leaks scripts, styles, and entities; layer carefully or skip regex entirely.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Stripping tags is fine for plain text. Parsing HTML with regex is NOT — use a parser.
const plain = html.replace(/<[^>]+>/g, '');
Try it Yourself »

Discussion

Loading…