StockTicker Marquee: Real-Time Prices for Your Website

How to Build a StockTicker Marquee with JavaScriptA stock ticker marquee is a compact, attention-grabbing UI element that displays live or frequently updated stock prices, symbols, and small changes. In this article you’ll learn how to design, build, and deploy a responsive, accessible, and efficient StockTicker marquee using vanilla JavaScript, CSS, and a simple server-side or third-party data source. We’ll cover architecture, data fetching and caching, smooth animation, accessibility, testing, and performance optimization.


Overview and goals

A good StockTicker marquee should:

  • Show current symbols, last prices, and change percent in a compact format.
  • Update frequently without jarring visual jumps.
  • Be responsive and lightweight so it can run on dashboards and websites.
  • Be accessible for assistive technologies and keyboard users.
  • Handle network issues gracefully and avoid excessive API calls.

This guide builds a marquee that:

  • Uses a public or mock API for price updates.
  • Animates horizontally in a continuous loop.
  • Updates data in-place (smoothly) without restarting the animation.
  • Falls back to cached data when network fails.

Project structure

Suggested file layout:

  • index.html
  • styles.css
  • ticker.js
  • data-provider.js (optional: wraps API calls)
  • server.js (optional: proxy or mock server)

Design considerations

  1. Data source

    • Use a reliable API (IEX Cloud, Alpha Vantage, Finnhub, Yahoo Finance, etc.) or a dedicated WebSocket feed for real-time data.
    • For production, prefer WebSockets or server-sent events for lower latency and fewer requests.
    • Respect API rate limits; implement client-side caching and a server proxy when needed.
  2. Data model Each ticker item should contain:

    • symbol (string)
    • price (number)
    • change (number)
    • changePercent (number)
    • timestamp
  3. UX & accessibility

    • Pause animation on hover and focus.
    • Provide keyboard controls to pause/resume.
    • Expose text alternatives for screen readers (aria-live regions).
    • Use color and icons to indicate up/down changes but not rely on color alone.
  4. Performance

    • Avoid frequent DOM reflows. Use transforms (translateX) for animation.
    • Update only the changed fields instead of rebuilding nodes.
    • Use requestAnimationFrame for JavaScript-driven animations when necessary.

HTML markup

Keep the HTML semantic and minimal. Example:

<!doctype html> <html lang="en"> <head>   <meta charset="utf-8" />   <meta name="viewport" content="width=device-width,initial-scale=1" />   <title>StockTicker Marquee</title>   <link rel="stylesheet" href="styles.css" /> </head> <body>   <header>     <h1>Stocks</h1>   </header>   <section class="ticker-wrap" aria-label="Stock Ticker">     <div class="ticker" id="stock-ticker" role="region" aria-live="polite"></div>   </section>   <script src="data-provider.js"></script>   <script src="ticker.js"></script> </body> </html> 

CSS: layout and animation

Use CSS for base styling and to enable smooth GPU-accelerated animation via transform.

:root {   --bg: #0f1724;   --text: #e6eef8;   --muted: #9fb0c8;   --up: #16a34a;   --down: #ef4444; } body {   margin: 0;   font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;   background: var(--bg);   color: var(--text); } .ticker-wrap {   overflow: hidden;   white-space: nowrap;   background: linear-gradient(90deg, rgba(255,255,255,0.03), rgba(255,255,255,0.01));   padding: 8px 12px; } .ticker {   display: inline-flex;   align-items: center;   gap: 28px;   will-change: transform; } .ticker-item {   display: inline-flex;   align-items: center;   gap: 8px;   font-size: 14px; } .symbol { font-weight: 700; color: var(--text); } .price { color: var(--muted); min-width: 64px; text-align: right; } .change { font-weight: 600; } .change.up { color: var(--up); } .change.down { color: var(--down); } /* Animation container that will be moved from right to left */ .ticker-animated {   display: inline-flex;   will-change: transform; } 

JavaScript: core logic

High-level flow:

  1. Fetch initial list of symbols and prices.
  2. Render ticker items into an inner scrolling container.
  3. Start an infinite animation that translates the inner container leftwards.
  4. Periodically fetch updates and update DOM fields in place.
  5. Loop the visible content by duplicating items to create a seamless scroll.

Key points:

  • Use CSS transforms for smooth movement.
  • Duplicate content to allow continuous looping.
  • Update only text content and classes for change direction.
  • Throttle update frequency and use exponential backoff on errors.

Example ticker.js (core parts):

// ticker.js const TICKER_ID = 'stock-ticker'; const SYMBOLS = ['AAPL','MSFT','GOOGL','AMZN','TSLA','NVDA','META','INTC']; const UPDATE_INTERVAL = 5000; // ms const SPEED = 60; // pixels per second const tickerEl = document.getElementById(TICKER_ID); let items = []; // current item data let startTime, animationFrameId; let offset = 0; async function fetchPrices(symbols) {   // Use data-provider.js which wraps fetch to your API or mock   try {     const res = await window.DataProvider.getPrices(symbols);     return res; // expected { symbol, price, change, changePercent, timestamp }[]   } catch (err) {     console.error('Price fetch error', err);     return null;   } } function createItemNode(item) {   const el = document.createElement('div');   el.className = 'ticker-item';   el.dataset.symbol = item.symbol;   el.innerHTML = `     <span class="symbol">${item.symbol}</span>     <span class="price">${item.price.toFixed(2)}</span>     <span class="change ${item.change >= 0 ? 'up' : 'down'}">       ${item.change >= 0 ? '▲' : '▼'} ${Math.abs(item.change).toFixed(2)}     </span>   `;   return el; } function render(itemsData) {   tickerEl.innerHTML = '';   const container = document.createElement('div');   container.className = 'ticker-animated';   itemsData.forEach(it => container.appendChild(createItemNode(it)));   // Duplicate for seamless loop   itemsData.forEach(it => container.appendChild(createItemNode(it)));   tickerEl.appendChild(container);   return container; } function startAnimation(container) {   const containerWidth = container.scrollWidth / 2; // width of a single set   const duration = containerWidth / SPEED * 1000;   let last = performance.now();   function step(now) {     const delta = now - last;     last = now;     offset += (SPEED * delta) / 1000; // px moved     if (offset >= containerWidth) offset -= containerWidth;     container.style.transform = `translateX(${-offset}px)`;     animationFrameId = requestAnimationFrame(step);   }   animationFrameId = requestAnimationFrame(step); } async function updateLoop() {   const data = await fetchPrices(SYMBOLS);   if (data) {     // Merge updates into items and update DOM nodes in-place     data.forEach(d => {       const node = tickerEl.querySelector(`.ticker-item[data-symbol="${d.symbol}"]`);       if (node) {         const priceEl = node.querySelector('.price');         const changeEl = node.querySelector('.change');         const prevPrice = parseFloat(priceEl.textContent) || d.price;         priceEl.textContent = d.price.toFixed(2);         const change = d.price - prevPrice;         changeEl.textContent = `${change >= 0 ? '▲' : '▼'} ${Math.abs(change).toFixed(2)}`;         changeEl.classList.toggle('up', change >= 0);         changeEl.classList.toggle('down', change < 0);       }     });   }   setTimeout(updateLoop, UPDATE_INTERVAL); } async function init() {   const data = await fetchPrices(SYMBOLS) || SYMBOLS.map(s => ({ symbol: s, price: 0, change: 0 }));   const container = render(data);   startAnimation(container);   updateLoop();   // Pause on hover/focus   tickerEl.addEventListener('mouseenter', () => cancelAnimationFrame(animationFrameId));   tickerEl.addEventListener('mouseleave', () => startAnimation(container)); } init(); 

Data provider: simple mock and live fetch

For development you can use a mock provider. For production, wrap a real API and implement caching and error handling.

Example data-provider.js (mock):

// data-provider.js window.DataProvider = {   async getPrices(symbols) {     // mock: random walk     return symbols.map(s => {       const base = (Math.random() * 150) + 50;       const change = (Math.random() - 0.5) * 2;       return {         symbol: s,         price: base + change,         change,         changePercent: (change / (base || 1)) * 100,         timestamp: Date.now()       };     });   } }; 

For a real API, implement a server-side proxy to hide API keys and combine multiple symbols into a single request.


Accessibility details

  • Use role=“region” and aria-label on the ticker container so screen readers can find it.
  • Use aria-live=“polite” or “off” depending on how disruptive live updates are.
  • Provide a visible and keyboard-focusable pause/play control to stop motion for users with vestibular sensitivity.
  • Avoid flashing color changes; use subtle transitions.

Example pause control:

<button id="ticker-toggle" aria-pressed="false">Pause</button> 

Add JS to toggle animation and update aria-pressed and button text.


Testing and debugging

  • Test with screen readers (NVDA, VoiceOver) to ensure announcements are reasonable.
  • Throttle network to test reconnection and caching behavior.
  • Use Lighthouse to check performance; aim to minimize layout shifts and wasted CPU.

Production tips

  • Use WebSockets or SSE for near real-time updates with lower latency and fewer requests.
  • Compress payloads and minimize fields returned by API.
  • Batch symbol requests server-side and cache results for a short TTL.
  • Respect user preferences for reduced motion: @media (prefers-reduced-motion: reduce) and pause animation accordingly.
  • Monitor API usage and implement exponential backoff on errors.

Example enhancements

  • Click to expand a symbol with a mini-chart.
  • Add grouping (indices, sectors) and filtering.
  • Provide currency conversion and localization for numbers.
  • Allow theme/sizing customization via CSS variables.

This covers a full approach to building a StockTicker marquee with JavaScript, from markup and styling to animation, data fetching, and accessibility. Implement the data provider with a real API and secure keys on the server for a production-ready ticker.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *