document.addEventListener('DOMContentLoaded', function () { const POLLING_INTERVAL = 3000; // 3 seconds // --- Configuration for Live Tickers --- const liveTickers = [ { symbol: 'BTCUSDT', elementId: 'live-crypto-row-btc', lastPrice: 0 }, { symbol: 'ETHUSDT', elementId: 'live-crypto-row-eth', lastPrice: 0 } ]; // --- Generic Fetch and Update Functions --- /** * Fetches the latest price for a given symbol and updates its corresponding row. * @param {object} ticker - The ticker object from the liveTickers array. * @param {HTMLElement} rowElement - The table row element to update. */ async function fetchAndupdate(ticker, rowElement) { try { const response = await fetch(`api/ticker.php?symbol=${ticker.symbol}`); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); if (data.error) { throw new Error(`API Error: ${data.error}`); } updateRow(data, ticker, rowElement); } catch (error) { console.error(`Failed to fetch live price for ${ticker.symbol}:`, error); // Optionally, display an error state in the specific row const priceCell = rowElement.querySelector('.price'); if (priceCell) priceCell.textContent = 'Error'; } } /** * Updates the DOM of a specific row with new data. * @param {object} data - The data object from the API. * @param {object} ticker - The ticker object being updated. * @param {HTMLElement} rowElement - The table row element. */ function updateRow(data, ticker, rowElement) { const priceCell = rowElement.querySelector('.price'); const changeCell = rowElement.querySelector('.change'); const symbolCell = rowElement.querySelector('.symbol'); const exchangeCell = rowElement.querySelector('.exchange'); if (!priceCell || !changeCell || !symbolCell || !exchangeCell) { console.error(`One or more required elements not found in row for ${ticker.symbol}`); return; } const currentPrice = parseFloat(data.price); const priceChange = currentPrice - ticker.lastPrice; // Update text content priceCell.textContent = `$${currentPrice.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; symbolCell.textContent = data.symbol.replace('_SPBL', ''); exchangeCell.textContent = data.exchange; // Update 24h change const changePercent = parseFloat(data.change_24h_percent); changeCell.textContent = `${changePercent.toFixed(2)}%`; changeCell.classList.toggle('text-success', changePercent >= 0); changeCell.classList.toggle('text-danger', changePercent < 0); // Visual flash on price change if (ticker.lastPrice !== 0 && priceChange !== 0) { const flashClass = priceChange > 0 ? 'flash-success' : 'flash-danger'; priceCell.classList.add(flashClass); setTimeout(() => priceCell.classList.remove(flashClass), 750); } ticker.lastPrice = currentPrice; // Update the last price for this specific ticker } // --- Initialization --- /** * Initializes the polling for all configured tickers. */ function initializeLiveTickers() { liveTickers.forEach(ticker => { const rowElement = document.getElementById(ticker.elementId); if (!rowElement) { console.error(`Element with ID #${ticker.elementId} not found for symbol ${ticker.symbol}.`); return; // Skip this ticker if its row doesn't exist } // Initial fetch fetchAndupdate(ticker, rowElement); // Start polling every X seconds setInterval(() => fetchAndupdate(ticker, rowElement), POLLING_INTERVAL); }); } initializeLiveTickers(); });