Search Engine Ranking Fluctuation Checker

Search Engine Ranking Fluctuation Checker

Track and visualize keyword ranking changes over time.

Keyword Performance Report

Ranking History

Detailed Fluctuations

Keyword Current Rank Previous Rank Change

Keyword Ranking Data

Enter historical ranks as comma-separated numbers (e.g., 5, 4, 4, 6, 5). The last number is the current rank.

Biggest Gainer

${analysis.biggestGainer.name} (+${analysis.biggestGainer.change})

Biggest Loser

${analysis.biggestLoser.name} (${analysis.biggestLoser.change})

Average Position

${analysis.averagePosition}

`; // Table dashboardTableBody.innerHTML = ''; analysis.processedKeywords.forEach(kw => { const tr = document.createElement('tr'); tr.className = 'bg-white border-b hover:bg-gray-50'; let changeHtml = ''; if (kw.change > 0) { changeHtml = `â–² +${kw.change}`; } else if (kw.change < 0) { changeHtml = `â–¼ ${kw.change}`; } else { changeHtml = `-`; } tr.innerHTML = ` ${kw.name} ${kw.currentRank} ${kw.prevRank} ${changeHtml} `; dashboardTableBody.appendChild(tr); }); // Chart chartKeywordSelect.innerHTML = state.keywords.map(kw => ``).join(''); renderChart(state.keywords[0].id); }; const renderChart = (keywordId) => { const keyword = state.keywords.find(kw => kw.id == keywordId); if (!keyword) return; const ranks = parseRanks(keyword.ranks).map(r => r === 0 ? null : r); // Handle 0 as no data point const labels = ranks.map((_, i) => `Day ${i + 1}`); if (rankingChart) rankingChart.destroy(); rankingChart = new Chart(rankingChartCanvas, { type: 'line', data: { labels: labels, datasets: [{ label: 'Rank', data: ranks, borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', tension: 0.1, fill: true, pointRadius: 4, pointBackgroundColor: '#3b82f6' }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { reverse: true, beginAtZero: false, suggestedMin: 1 } }, plugins: { legend: { display: false } } } }); }; // --- Event Handlers --- addKeywordBtn.addEventListener('click', () => createKeywordRow()); chartKeywordSelect.addEventListener('change', (e) => renderChart(e.target.value)); // --- Tab & Navigation Logic --- function switchTab(targetTabId) { if (currentTab === 'config') { saveConfig(); } currentTab = targetTabId; tabs.forEach(tab => tab.classList.toggle('active', tab.dataset.tab === currentTab)); tabContents.forEach(content => content.classList.toggle('active', content.id === currentTab)); if (currentTab === 'dashboard') { renderDashboard(); } else { renderConfig(); } updateNavButtons(); } function updateNavButtons() { if (currentTab === 'dashboard') { nextBtn.textContent = 'Configure Data'; prevBtn.style.display = 'none'; pdfButtonContainer.style.display = 'block'; } else { nextBtn.textContent = 'Update Dashboard'; prevBtn.style.display = 'inline-flex'; pdfButtonContainer.style.display = 'none'; } } tabs.forEach(tab => tab.addEventListener('click', () => switchTab(tab.dataset.tab))); nextBtn.addEventListener('click', () => currentTab === 'dashboard' ? switchTab('config') : switchTab('dashboard')); prevBtn.addEventListener('click', () => currentTab === 'config' ? switchTab('dashboard') : switchTab('config')); // --- PDF Download --- downloadPdfBtn.addEventListener('click', async () => { const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const content = document.getElementById('pdf-content'); document.getElementById('pdf-date').textContent = `Report Generated: ${new Date().toLocaleString()}`; const canvas = await html2canvas(content, { scale: 2 }); const imgData = canvas.toDataURL('image/jpeg', 1.0); const imgProps = doc.getImageProperties(imgData); const pdfWidth = doc.internal.pageSize.getWidth() - 28; const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width; doc.addImage(imgData, 'JPEG', 14, 15, pdfWidth, pdfHeight); document.getElementById('pdf-date').textContent = ''; doc.save(`Ranking-Fluctuation-Report-${new Date().toISOString().slice(0,10)}.pdf`); }); // --- Initial Load --- renderDashboard(); updateNavButtons(); });
Scroll to Top