Reading Data from an Existing Web App with a Chrome Content Script
TIL that a content script reading order data straight from the DOM breaks every time the POS ships a markup change — the fix was reading from the most stable layer available, not the most convenient one.
The Problem #
The extension needed order details out of the POS's own web page, and I had no API access to that system — a content script reading the rendered DOM was the only option. The first version queried elements by their CSS classes, which worked until the POS shipped an unrelated styling update and every class name changed.
Context #
This is Recaho's own web app, not something I control or get advance notice about — any markup change on their end is invisible to me until the extension breaks in production.
What I Tried #
Patched the selectors to match the new class names every time something broke.
What Went Wrong #
This was reactive by construction — I only found out a selector broke when a driver reported missing order data, meaning there was always a window of silent failure between a POS update and my fix shipping.
The Solution #
Two changes: prefer more stable selection strategies over exact class names where possible (semantic attributes, structural position, visible text patterns), and add a runtime check that logs — rather than silently returns empty — when expected data can't be found, so breakage is detected immediately instead of via a support report.
function extractOrderId() {
// Prefer a data attribute or stable structural pattern over a
// CSS class, which is far more likely to change on a style pass.
const el = document.querySelector("[data-order-id]") ?? document.querySelector(".order-header .id");
if (!el) {
console.error("[extension] Could not locate order ID — POS markup may have changed");
return null;
}
return el.textContent.trim();
}Why It Works #
CSS classes are the layer of a page most likely to change for reasons that have nothing to do with the data they happen to sit next to (a redesign, a CSS framework migration). Data attributes and structural/semantic patterns are more likely to survive a purely visual change, and explicit failure logging turns "silently broken" into "immediately visible," which is the difference between finding out from a driver and finding out from a log.
Lessons Learned #
Integrating with a system you don't control means every selector is a bet on how stable that particular piece of markup is, and the bet should be made deliberately — not just by grabbing whatever selector happened to work in DevTools that day.
What I Would Do Differently #
I'd build the "log loudly when expected data is missing" check in from the first version, instead of adding it only after the first silent breakage.
Related Concepts #
DOM scraping resilience, CSS selector stability, defensive logging for third-party integrations.
Related content
Writing a Reliable Regex for Extracting Delivery Times
TIL that a regex tuned against a handful of examples ('DELIVERY TIME: 2:00 PM') broke on real data within a day because of extra whitespace and inconsistent AM/PM casing I hadn't accounted for.
Extracting Delivery Information from Dynamically Rendered HTML
TIL that a content script querying the DOM immediately on page load found nothing, because the POS's order details render client-side, after the content script's own 'document_idle' point had already passed.
Injecting Custom UI Into a Website You Don't Own
TIL that a naive injected panel kept getting wiped out by the host page's own re-renders — the fix was watching for that, not fighting it, and mounting into a container the host page has no reason to touch.