Source Code

<div class="wrap">
  <div class="table-head">
    <h2 class="table-title">Watchlist</h2>
    <span class="live-dot"><i></i>Live</span>
  </div>
  <div class="table-scroll">
    <table class="tbl">
      <thead>
        <tr>
          <th>Symbol</th>
          <th class="num">Price</th>
          <th class="num">24h change</th>
          <th class="spark-col">Trend</th>
        </tr>
      </thead>
      <tbody id="tbody"></tbody>
    </table>
  </div>
</div>

Live Market Watchlist Table — Free HTML CSS JS Snippet

Live Market Watchlist Table · Tables · Plain HTML, CSS & JS · Live preview

What's included

Features

Live simulated price stream updates every 1.6 seconds via a repeating interval
Row briefly flashes green or red matching the direction of that specific price change
Forced reflow (offsetWidth read) restarts the flash CSS transition even on consecutive same-direction moves
Per-asset sparkline normalizes to its own rolling price history, not a shared fixed scale
Percentage change always measured against a fixed session-start baseline, not the previous tick
Only the changed price, badge, and sparkline DOM nodes update per tick — the table body is never fully re-rendered after initial load
Rolling 12-point history window keeps each sparkline focused on recent movement
Up/down direction reinforced with both color and an arrow glyph for redundant signaling

About this UI Snippet

Live Market Watchlist Table — Flash-on-Change Rows, Live Sparklines & Surgical DOM Updates

Screenshot of the Live Market Watchlist Table snippet rendered live

A price table that silently updates numbers in place is easy to miss changes in — a trader or investor glancing away for a few seconds has no way to tell whether a price just moved or has been sitting still the whole time. This watchlist solves that with a brief background-color flash on any row whose price just changed, colored green or red by direction, alongside a live sparkline trend line per asset that updates every tick.

Surgical per-cell updates, not a full re-render

render() builds the entire table once on load, but the repeating updatePrices() function never calls it again. Instead, it looks up each row by data-symbol, then updates exactly three things inside it directly: the price text node, the change badge's class and innerHTML, and the sparkline polyline's points and stroke attributes. This distinction is what makes the flash animation possible at all — if the whole table re-rendered from scratch every tick, any CSS transition or flash class added a moment earlier would be wiped out along with the stale DOM node it was attached to.

Restarting a CSS transition with a forced reflow

When a row's price moves in the *same* direction two ticks in a row, simply re-adding the same .flash-up class would have no visible effect — the class is already present, so no transition triggers. updatePrices() handles this by removing both flash classes, reading row.offsetWidth (a layout property read that forces the browser to flush pending style changes before continuing — a well-known technique for restarting CSS animations), and only then re-adding the appropriate flash class. Without that forced reflow, a stock moving up for three consecutive ticks would only flash on the first one.

Self-normalizing sparklines from a rolling history window

Each asset carries its own history array, capped at 12 points via .shift() once new prices push it over that length. sparkPoints() normalizes each point between that array's own current min and max, so the sparkline always uses its full available height regardless of whether the asset has been range-bound or has just had a sharp move — a fixed y-axis scale shared across all assets would flatten a low-volatility asset's line to a nearly straight one.

Percent change measured against a fixed baseline, not the previous tick

pctChange() always compares the current price to basePrices, a value captured once when the page loaded (the first entry in each asset's initial history) — not to the price one tick earlier. This mirrors how a real "24h change" figure works: it should represent total movement since a fixed reference point, not reset itself relative to whatever the price happened to be a moment ago.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why updatePrices reads row.offsetWidth before re-adding a flash class, and what visual bug would appear on consecutive same-direction price moves if that line were removed. The same assistant can help optimize it — for instance asking whether querying the DOM by data-symbol on every single tick for every asset is efficient enough at a larger watchlist size, or whether caching row/element references up front would be worth the added bookkeeping. It's also useful for extending the table: ask it to add a sortable "24h change" column, a search/filter input across symbols, or wire the whole thing to a real WebSocket price feed with reconnect handling. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a live-updating "market watchlist" table in plain HTML, CSS, and JavaScript — no charting library, no framework.

Requirements:
- Maintain a plain array of asset objects, each with a symbol, a display name, a current price, and a rolling array of recent price history values (capped at a fixed length, dropping the oldest value as new ones are added).
- Render the initial table once from that array, with each row showing the symbol/name, the current price formatted as currency, a percentage-change badge (colored and arrow-marked for up versus down), and a small inline SVG sparkline of that asset's own price history.
- Simulate a live price feed with a repeating timer that nudges each asset's price by a small random amount. On every tick, update only the specific DOM nodes for the price text, the change badge, and the sparkline of each affected row directly — do not rebuild or re-render the entire table body from the array again after the initial render.
- Whenever a specific row's price actually changes, briefly flash that row's background color (green if it went up, red if it went down) using a CSS transition, and make sure the flash reliably re-triggers even if the price moves in the same direction on consecutive updates (research and use the standard technique for forcing a CSS transition restart, since simply re-adding an already-present class will not retrigger a transition).
- The percentage-change figure must be calculated against a single fixed reference price captured once when the page loads for each asset (not against whatever the price was on the immediately preceding tick).
- Normalize each row's sparkline points to that specific asset's own current minimum and maximum history values (not a scale shared across all assets), so the line's vertical range always uses the chart's full height regardless of how volatile or stable that particular asset has been.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Watch it update liveEvery 1.6 seconds, prices nudge randomly — the changed price, percentage badge, and sparkline update in place, with the row briefly flashing green or red.
  2. 2
    Replace ASSETS with real symbolsSet each asset's symbol, name, starting price, and initial history array to real data from your market data provider.
  3. 3
    Connect a real price feedReplace the setInterval(updatePrices, 1600) simulation with a WebSocket or polling callback from your market data API that sets a.price directly before running the same DOM-update logic.
  4. 4
    Adjust the flash durationChange the 550 in the setTimeout call and the 0.5s in the CSS transition to make the flash linger longer or shorter.
  5. 5
    Change the sparkline history windowEdit the "if (a.history.length > 12)" check to show a longer or shorter recent trend per asset.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a Tailwind CSS version.

Real-world uses

Common Use Cases

Crypto and stock trading dashboards
The core use case — a personal or platform watchlist showing live price movement across multiple assets at a glance.
Portfolio tracking apps
Adapt the same flash-and-sparkline pattern to show live gain/loss per holding in a personal investment tracker.
Trading terminal and market data widgets
Embed as one panel inside a larger trading terminal alongside order books and charts.
Learn surgical live-update DOM patterns
A clean example of updating only the DOM nodes that actually changed on each tick, instead of re-rendering an entire list — directly transferable to any live-data table.
Sports odds and live-score tables
Repurpose the same flash-on-change and per-row trend pattern for live betting odds or sports score tickers instead of asset prices.

Got questions?

Frequently Asked Questions

If a row's price moves in the same direction on two consecutive ticks, simply re-adding the already-present .flash-up class triggers no CSS transition, since the class never actually left. Reading row.offsetWidth forces the browser to apply the class-removal style change immediately (a technique called a forced reflow) before the class is re-added, guaranteeing the transition restarts every time.

Rebuilding the entire tbody from scratch every 1.6 seconds would destroy and recreate every row, which would immediately erase any in-progress flash CSS transition and cause needless layout work. updatePrices() instead finds each row by its data-symbol attribute and updates only the price text, change badge, and sparkline polyline points directly.

pctChange() always compares the current price to basePrices, a value captured once from each asset's initial history when the page first loaded — not the price one tick earlier. This matches how a real "24h change" figure behaves: a running total change since a fixed reference point, not something that resets relative to the last tick.

sparkPoints() normalizes each asset's own history array between that array's current minimum and maximum, so every sparkline always uses its full available height — a shared fixed scale across assets would flatten a low-volatility asset's line to nearly a straight one.

Replace the setInterval(updatePrices, 1600) simulation with a WebSocket message handler (or polling call) from your market data provider that sets each asset's price property to the real incoming value, then reuses the same DOM-update block that already exists inside updatePrices() for the flash, badge, and sparkline logic.

Each asset's history array is capped at 12 entries via a .shift() call once a new price push exceeds that length, keeping the sparkline focused on roughly the most recent 12 ticks rather than growing unbounded.