HTML 在线预览 实时编辑 · 所见即所得
纯前端 · 无依赖
📝 HTML
模板: 0 行
👁 预览 实时
✨ 输入代码后自动预览

你好,世界!

在左侧编辑 HTML/CSS,右侧实时预览效果。

`, navbar: ` 导航栏示例

欢迎来到我的网站

这是一个使用 Flexbox 构建的响应式导航栏示例。鼠标悬停或点击导航链接,可以查看交互效果。

`, card: ` 卡片示例
🌄

自然风光

探索大自然的壮丽美景,感受山川湖海的宁静与力量。

#旅行
🚀

科技创新

了解最新科技趋势,从人工智能到太空探索的前沿资讯。

#科技
📚

知识分享

阅读精选文章与教程,持续学习、不断提升自我。

#学习
`, form: ` 表单示例

注册新账号

填写信息完成注册

` }; // ========== State ========== let debounceTimer = null; let dragState = null; // ========== Core: render preview ========== function renderPreview() { const code = editor.value; if (!code.trim()) { frame.srcdoc = ''; previewEmpty.style.display = 'flex'; statusDot.style.background = 'var(--text-muted)'; statusText.textContent = '空代码'; return; } previewEmpty.style.display = 'none'; frame.srcdoc = code; statusDot.style.background = 'var(--success)'; statusText.textContent = '已渲染'; } // ========== Input handler with debounce ========== function onEditorInput() { updateLineCount(); statusDot.style.background = '#f1c40f'; statusText.textContent = '等待...'; clearTimeout(debounceTimer); debounceTimer = setTimeout(renderPreview, 300); } // ========== Update line count ========== function updateLineCount() { const lines = editor.value.split('\n').length; lineCount.textContent = lines + ' 行'; } // ========== Toast ========== let toastTimer = null; function showToast(msg, type) { toast.textContent = msg; toast.className = 'toast'; if (type === 'success') toast.classList.add('toast-success'); if (type === 'error') toast.classList.add('toast-error'); // force reflow void toast.offsetWidth; toast.classList.add('show'); clearTimeout(toastTimer); toastTimer = setTimeout(function() { toast.classList.remove('show'); }, 2200); } // ========== Copy code ========== function copyCode() { if (!editor.value.trim()) { showToast('没有可复制的内容', 'error'); return; } if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(editor.value).then(function() { showToast('✅ 代码已复制到剪贴板', 'success'); }).catch(function() { fallbackCopy(); }); } else { fallbackCopy(); } } function fallbackCopy() { editor.select(); try { document.execCommand('copy'); showToast('✅ 代码已复制到剪贴板', 'success'); } catch(e) { showToast('❌ 复制失败,请手动选择复制', 'error'); } } // ========== Clear code ========== function clearCode() { if (!editor.value.trim()) return; if (confirm('确定要清空当前代码吗?')) { editor.value = ''; updateLineCount(); renderPreview(); showToast('已清空代码', 'success'); } } // ========== Load template ========== function loadTemplate(value) { if (TEMPLATES[value]) { editor.value = TEMPLATES[value]; updateLineCount(); renderPreview(); showToast('已加载模板:' + templateSelect.options[templateSelect.selectedIndex].text, 'success'); } } // ========== Open in new window ========== function openInNewWindow() { const code = editor.value.trim(); if (!code) { showToast('请先编写 HTML 代码', 'error'); return; } const win = window.open('', '_blank'); if (!win) { showToast('❌ 弹窗被拦截,请允许弹窗', 'error'); return; } win.document.write(code); win.document.close(); } // ========== Keyboard shortcuts ========== function onKeydown(e) { // Ctrl+Enter -> Run if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); renderPreview(); showToast('已刷新预览', 'success'); } // Ctrl+Shift+C -> Copy if ((e.ctrlKey || e.metaKey) && e.shiftKey && (e.key === 'c' || e.key === 'C')) { e.preventDefault(); copyCode(); } } // ========== Divider drag ========== function initDivider() { divider.addEventListener('mousedown', function(e) { e.preventDefault(); dragState = { startX: e.clientX, startY: e.clientY, startLeft: document.getElementById('editor-panel').offsetWidth, startTop: document.getElementById('editor-panel').offsetHeight }; divider.classList.add('active'); document.body.style.cursor = 'col-resize'; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', function(e) { if (!dragState) return; const isMobile = window.innerWidth <= 768; if (isMobile) { // vertical layout: resize height const delta = e.clientY - dragState.startY; const totalHeight = document.querySelector('.main').offsetHeight; const pct = ((dragState.startTop + delta) / totalHeight) * 100; document.getElementById('editor-panel').style.flex = '0 0 ' + Math.max(20, Math.min(80, pct)) + '%'; } else { // horizontal layout: resize width const delta = e.clientX - dragState.startX; const totalWidth = document.querySelector('.main').offsetWidth; const pct = ((dragState.startLeft + delta) / totalWidth) * 100; document.getElementById('editor-panel').style.flex = '0 0 ' + Math.max(20, Math.min(80, pct)) + '%'; } }); document.addEventListener('mouseup', function() { if (!dragState) return; dragState = null; divider.classList.remove('active'); document.body.style.cursor = ''; document.body.style.userSelect = ''; }); } // ========== Bootstrap ========== function init() { // Load blank template as default editor.value = TEMPLATES.blank; updateLineCount(); renderPreview(); // Event listeners editor.addEventListener('input', onEditorInput); editor.addEventListener('keydown', onKeydown); runBtn.addEventListener('click', function() { renderPreview(); showToast('已刷新预览', 'success'); }); copyBtn.addEventListener('click', copyCode); clearBtn.addEventListener('click', clearCode); openBtn.addEventListener('click', openInNewWindow); templateSelect.addEventListener('change', function() { loadTemplate(this.value); }); // Window resize re-check window.addEventListener('resize', function() { if (window.innerWidth <= 768) { document.body.style.cursor = ''; document.body.style.userSelect = ''; } }); initDivider(); } // Start when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();