功能介绍
HTML+JS开发一个网站状态检测网站,用来监测网站是否可以正常访问,可以快速知道自己网站运行状态。
1. 网址管理功能
添加网址:用户可以在输入框中输入要监测的网页地址,点击 “添加网址” 按钮,工具会对输入的网址进行格式验证。验证通过后,网址会被添加到左侧面板的列表中,并且列表项后有 “删除” 按钮。
删除网址:在左侧面板的网址列表中,每个网址后面都有一个 “删除” 按钮,点击该按钮可以将对应的网址从监测列表中移除,同时也会从筛选下拉框中移除该网址选项。
筛选网址:右侧面板有一个 “筛选网址” 的下拉框,用户可以选择具体的网址进行筛选,只显示该网址的监测日志,也可以选择 “全部” 来显示所有网址的监测日志。
2. 监测功能
设置监测间隔:用户可以在输入框中设置监测间隔时间(单位为秒),默认值为 60 秒。
开始 / 停止监测:点击 “开始监测” 按钮,工具会立即对所有已添加的网址进行一次检测,之后按照设置的监测间隔时间循环检测。点击 “停止监测” 按钮可停止监测。
重试机制:在进行网址检测时,如果请求失败,会进行最多 3 次重试,若重试后仍失败,则记录错误日志。
3. 日志记录与显示功能
日志记录:每次对网址进行检测后,会记录网址的状态(正常或异常)、响应时间、时间戳以及错误信息(若有)。日志记录会存储在 logs 数组中,当日志数量超过 1000 条时,会移除最早的日志记录。
日志显示:右侧面板的日志容器会显示所有或筛选后的监测日志,正常状态的日志为黑色,异常状态的日志为红色。日志会自动滚动到最底部以显示最新信息。
代码分享
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>网站监测工具</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f9f9f9; display: flex; margin: 0; padding: 0; height: 100vh; } /* 调整左右面板的高度,减去一行日志的大致高度(假设为 30px) */ .left-panel, .right-panel { flex: 1; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); margin: 20px; height: calc(100vh - 40px - 30px); overflow-y: auto; display: flex; flex-direction: column; } .right-panel { flex: 2; } h1 { color: #333; margin-bottom: 20px; text-align: center; } .input-container { margin-bottom: 15px; display: flex; align-items: center; } input { padding: 10px; margin-right: 10px; border: 1px solid #ccc; border-radius: 4px; flex: 1; transition: all 0.3s ease; } input.invalid { color: red; animation: shake 0.5s ease-in-out 3; } @keyframes shake { 0% { transform: translateX(0); } 25% { transform: translateX(-5px); } 50% { transform: translateX(5px); } 75% { transform: translateX(-5px); } 100% { transform: translateX(0); } } button { padding: 10px 15px; background-color: #007BFF; color: white; border: none; border-radius: 4px; cursor: pointer; transition: background-color 0.3s ease; } button:hover { background-color: #0056b3; } #monitor-button.started { background-color: #dc3545; } ul { list-style-type: none; padding: 0; margin-top: 10px; /* 假设每个网址高度约 30px,显示 5 个网址的高度 */ height: calc(5 * (30px + 5px)); overflow-y: auto; flex: 1; } li { background-color: #f4f4f9; padding: 10px; margin-bottom: 5px; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; } #log-container { margin-top: 10px; height: calc(11 * (30px + 5px)); overflow-y: auto; border: 1px solid #ccc; padding: 10px; border-radius: 4px; } #filter-container { margin-bottom: 10px; } #filter-url { padding: 8px; border: 1px solid #ccc; border-radius: 4px; width: 100%; } </style> </head> <body> <div> <h1>网站监测工具</h1> <div> <input type="text" id="url-input" placeholder="输入要监测的网页地址"> <button id="add-url">添加网址</button> </div> <div> <label for="interval">监测间隔 (秒):</label> <input type="number" id="interval" value="60"> <button id="monitor-button">开始监测</button> </div> <ul id="url-list"></ul> </div> <div> <div id="filter-container"> <label for="filter-url">筛选网址:</label> <select id="filter-url"> <option value="">全部</option> </select> </div> <div id="log-container"></div> </div> <script> const urlInput = document.getElementById('url-input'); const addUrlButton = document.getElementById('add-url'); const intervalInput = document.getElementById('interval'); const monitorButton = document.getElementById('monitor-button'); const urlList = document.getElementById('url-list'); const logContainer = document.getElementById('log-container'); const filterUrlSelect = document.getElementById('filter-url'); let urls = []; let logs = []; let intervalId; let isMonitoring = false; const MAX_RETRIES = 3; const MAX_LOGS = 1000; let isNetworkConnected = true; addUrlButton.addEventListener('click', () => { let url = urlInput.value.trim(); if (!url) { urlInput.classList.add('invalid'); setTimeout(() => { urlInput.classList.remove('invalid'); }, 1500); alert('请输入有效的网址'); return; } if (!url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('www.')) { url = 'https://' + url; } else if (url.startsWith('www.')) { url = 'https://' + url; } if (!/^https?:\/\/.+/i.test(url)) { urlInput.classList.add('invalid'); setTimeout(() => { urlInput.classList.remove('invalid'); }, 1500); alert('请输入以 http://、https:// 或 www. 开头的有效网址'); return; } urlInput.classList.remove('invalid'); urls.push(url); const li = document.createElement('li'); li.textContent = url; const deleteButton = document.createElement('button'); deleteButton.textContent = '删除'; deleteButton.addEventListener('click', () => { const index = urls.indexOf(url); if (index > -1) { urls.splice(index, 1); urlList.removeChild(li); const options = Array.from(filterUrlSelect.options); const optionToRemove = options.find(option => option.value === url); if (optionToRemove) { filterUrlSelect.removeChild(optionToRemove); } } }); li.appendChild(deleteButton); urlList.appendChild(li); urlInput.value = ''; const option = document.createElement('option'); option.value = url; option.textContent = url; filterUrlSelect.appendChild(option); }); monitorButton.addEventListener('click', () => { if (!isMonitoring) { const interval = parseInt(intervalInput.value) * 1000; // 立即检测一次所有添加的网址 urls.forEach(url => { checkUrl(url, 0); }); // 按间隔时间循环检测 intervalId = setInterval(() => { const newInterval = parseInt(intervalInput.value) * 1000; if (newInterval!== interval) { clearInterval(intervalId); intervalId = setInterval(() => { urls.forEach(url => { checkUrl(url, 0); }); }, newInterval); } if (isNetworkConnected) { urls.forEach(url => { checkUrl(url, 0); }); } }, interval); monitorButton.classList.add('started'); monitorButton.textContent = '停止监测'; isMonitoring = true; } else { clearInterval(intervalId); monitorButton.classList.remove('started'); monitorButton.textContent = '开始监测'; isMonitoring = false; } }); filterUrlSelect.addEventListener('change', () => { const selectedUrl = filterUrlSelect.value; displayLogs(selectedUrl); }); function monitorUrlsImmediately(urls, interval) { // 先立即执行一次监测 urls.forEach(url => { checkUrl(url, 0); }); // 再按间隔时间循环监测 intervalId = setInterval(() => { urls.forEach(url => { checkUrl(url, 0); }); }, interval); } function checkUrl(url, retryCount) { if (!isNetworkConnected) { handleLog(url, null, '电脑网络连接异常'); return; } const startTime = performance.now(); const controller = new AbortController(); const signal = controller.signal; const timeoutId = setTimeout(() => { controller.abort(); }, 5000); const requestOptions = { mode: 'cors', signal: signal, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } }; fetch(url, requestOptions) .then(() => { clearTimeout(timeoutId); const endTime = performance.now(); const responseTime = endTime - startTime; handleLog(url, responseTime, null); isNetworkConnected = true; }) .catch(error => { clearTimeout(timeoutId); const endTime = performance.now(); const responseTime = endTime - startTime; if (retryCount < MAX_RETRIES) { setTimeout(() => { checkUrl(url, retryCount + 1); }, 1000); } else { let errorMessage = error.message; if (error.name === 'AbortError') { errorMessage = '请求超时'; } else if (error.message.includes('NetworkError')) { errorMessage = '网络错误,请检查网络连接或dai理设置'; isNetworkConnected = false; } handleLog(url, responseTime, errorMessage); } }); } function handleLog(url, responseTime, errorMessage) { let status; if (errorMessage === '电脑网络连接异常') { status = '异常'; } else if (responseTime!== null && responseTime < 800) { status = '正常'; errorMessage = null; } else { status = '异常'; if (!errorMessage) { errorMessage = `响应时间过长: ${responseTime} 毫秒`; } } const log = { url, status, responseTime: responseTime!== null? Math.round(responseTime) : null, timestamp: new Date().toLocaleString(), error: errorMessage }; logs.push(log); if (logs.length > MAX_LOGS) { logs.shift(); } displayLogs(filterUrlSelect.value); updateUrlColor(url, status === '正常'); } function updateUrlColor(url, isNormal) { const listItems = urlList.getElementsByTagName('li'); for (let i = 0; i < listItems.length; i++) { if (listItems[i].textContent.includes(url)) { if (isNormal) { listItems[i].style.color = 'black'; } else { listItems[i].style.color = 'red'; } break; } } } function displayLogs(filterUrl) { logContainer.innerHTML = ''; const filteredLogs = filterUrl? logs.filter(log => log.url === filterUrl) : logs; filteredLogs.forEach(log => { const logEntry = document.createElement('p'); let statusColor; if (log.status === '正常') { statusColor = 'black'; } else { statusColor = 'red'; } logEntry.style.color = statusColor; logEntry.textContent = `${log.timestamp} - ${log.url} - 状态: ${log.status}`; if (log.responseTime!== null) { logEntry.textContent += ` - 响应时间: ${log.responseTime} 毫秒`; } if (log.error) { logEntry.textContent += ` - 错误信息: ${log.error}`; } logContainer.appendChild(logEntry); }); // 自动滚动到最底部显示最新信息 logContainer.scrollTop = logContainer.scrollHeight; } </script> </body> </html>