马黑黑 发表于 2025-5-25 21:42

简易HTML编辑器

<style>
        iframe { position: relative; width: 100%; height: 100%; border: none; outline: none; box-sizing: border-box; margin: 0; }
        .wrap{ left:calc(50% - 81px); transform:translateX(-50%);width:1400px; height:700px; background:linear-gradient(to right,#eee 59px,#aaa 60px,#fff 0); border:1px solid gray; border-radius:4px; box-shadow:2px 2px 6px gray; z-index: 1; position:relative; margin: 30px 0; }
        @media screen and (max-width: 1280px) {        .wrap { width: 90vw; height: 75vh; font-size: 12px; } }
        .wrap:hover{ border-color: black; }
        .wrap:has(#editor:focus) { border-width:2px; }
        .same{ position: absolute; top:10px; box-sizing: border-box; padding: 8px; border:none; border-radius: inherit; white-space: pre-wrap; word-break: break-all; font: normal 16px/24px Consolas,'Courier New','Andale Mono',monospace; tab-size: 4; overflow: auto; }
        #editor{ right: 0; width: calc(100% - 60px); height: calc(100% - 20px); resize: none; outline: none; background: #fefefe; scroll-padding: 8px;}
        #line-number{ position: absolute; width: 100%; height: calc(100% - 20px); color: gray; user-select: none; }
        #line-number > span{cursor:pointer;}
        #line-number > span:hover{ font-weight: bold; color: red; }
        #mnbox{ min-height: 0; visibility: hidden; }
        #showBox { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: #fff; display: none; padding: 0; overflow: hidden; z-index: 10000; margin: 0; }
        #showBox::after { position: absolute; content: '关闭预览'; bottom: 10px; left: calc(50% - 40px); padding: 0 4px; width: 80px; height: 30px; line-height: 30px; text-align: center; border: 1px solid #efe; border-radius: 6px; background: #eee; font-size: 14px; box-shadow: 2px 2px 6px rgba(0,0,0,.25); cursor: pointer; }
        #btnPrev { padding: 2px 8px; cursor: pointer; border: 1px solid #efe; border-radius: 4px; box-shadow:2px 2px 6px rgba(0,0,0,.25); font-size: 14px; }
        #btnPrev:hover { color: red; }
</style>

<h2>Web代码简易编辑器</h2>
<div class="wrap">
        <div id="mnbox" class="same"></div>
        <div id="line-number" class="same"></div>
        <textarea id="editor" class="same" placeholder="输入CSS+HTML+JS代码..."></textarea>
</div>
<p><button type="button" id="btnPrev" title="预览效果">运行代码</button></p>
<div id="showBox" title="点击关闭"></div>

<script>
let isSyncing = false;

const lnum = document.getElementById('line-number');
const editor = document.getElementById('editor');
const mnbox = document.getElementById('mnbox');
const btnPrev = document.getElementById('btnPrev');

const selectLine = (num) => {
        let mpos, tstr = '';
        let ar = editor.value.split('\n');
        for (j = 0; j < num; j ++) {
                tstr += ar + 2;
        }
        mpos = tstr.length;
        editor.setSelectionRange(mpos, mpos + ar.length);
        editor.focus();
};

const addLineNum = () => {
        const lines = editor.value.split('\n');
        let str = '';
        lines.forEach((line, key) => {
                let br = '<br>'.repeat(calcSolftLines(line) || 1);
                let idx = (key + 1).toString().padStart(4,' ');
                str += `<span onclick="selectLine(${key})">${idx}</span>${br}`;
        });
        lnum.innerHTML = str;
};

const calcSolftLines = (text) => {
        const cssdata = window.getComputedStyle(editor);
        mnbox.style.width = editor.offsetWidth + 'px';
        mnbox.style.overflowY = editor.scrollTop > 0 ? 'scroll' : 'hidden';
        mnbox.innerText = text;
        const lineheight = parseFloat(cssdata.getPropertyValue('line-height'));
        const padding = parseFloat(cssdata.getPropertyValue('padding'));
        return Math.floor((mnbox.offsetHeight - padding * 2) / lineheight);
};

const getCurrentLineIndent = () => {
        const cursorPos = editor.selectionStart;
        const content = editor.value;
        let lineStart = content.lastIndexOf('\n', cursorPos - 1) + 1;
        const line = content.slice(lineStart, cursorPos);
        return line.match(/^[\t ]*/)?. || '';
};

const insertTextAtCursor = (text) => {
        const start = editor.selectionStart;
        const end = editor.selectionEnd;
        const content = editor.value;
        editor.value = content.slice(0, start) + text + content.slice(end);
        editor.selectionStart = editor.selectionEnd = start + text.length;
};

const preView = (htmlCode, targetBox) => {
        if (targetBox.innerHTML) return;
console.log('Yes');
        window.localStorage.setItem('ed1', htmlCode);
        const iframe = document.createElement('iframe');
        htmlCode = htmlCode + '\n<style>body {margin: 0; }</style>\n';
        iframe.srcdoc = htmlCode;
        targetBox.appendChild(iframe);
        targetBox.style.display = 'block';
        targetBox.onclick = () => {
                targetBox.innerHTML = '';
            targetBox.style.display = 'none';
        }
};

btnPrev.onclick = () => preView(editor.value, showBox);

editor.onkeydown = (e) => {
        if (e.key === 'Tab') {
                e.preventDefault();
                insertTextAtCursor('\t');
        }
        if (e.key === 'Enter') {
                e.preventDefault();
                const indent = getCurrentLineIndent();
                insertTextAtCursor('\n' + indent);
                addLineNum();
        }
};

const bindEventLnum = () => {
    removeEventEditor();
    lnum.addEventListener('scroll', lnumScroll);
};

const bindEventEditor = () => {
    removeEventLnum();
    editor.addEventListener('scroll', editorScroll);
};

const lnumScroll = () => {
    if (!isSyncing) {
      isSyncing = true;
      editor.scrollTop = lnum.scrollTop;
      isSyncing = false;
    }
};

const editorScroll = () => {
    if (!isSyncing) {
      isSyncing = true;
      lnum.scrollTop = editor.scrollTop;
      isSyncing = false;
    }
};

lnum.addEventListener('mouseover', bindEventLnum);
editor.addEventListener('mouseover', bindEventEditor);
lnum.addEventListener('touchstart', bindEventLnum, { passive: false });
editor.addEventListener('touchstart', bindEventEditor, { passive: false });

const removeEventLnum = () => lnum.removeEventListener('scroll', lnumScroll);
const removeEventEditor = () => editor.removeEventListener('scroll', editorScroll);

editor.oninput = () => addLineNum();
window.onresize = () => addLineNum();
editor.onmouseenter = () => editor.focus();
editor.value = window.localStorage.getItem('ed1');
editor.setSelectionRange(0, 0);
addLineNum();
</script>

马黑黑 发表于 2025-5-25 21:48

这个,可以在线测试帖子代码,css + HTML + JS 代码一般都可以预览

花飞飞 发表于 2025-5-25 21:59

这个好,刚我试的代码还保存着。直接运行了。。

红影 发表于 2025-5-25 21:59

这个真好,把黑黑前面讲解放到其中测试,感觉很方便{:4_187:}

马黑黑 发表于 2025-5-25 22:00

红影 发表于 2025-5-25 21:59
这个真好,把黑黑前面讲解放到其中测试,感觉很方便

就是得多看个浏览器标签

花飞飞 发表于 2025-5-25 22:00

马黑黑 发表于 2025-5-25 21:48
这个,可以在线测试帖子代码,css + HTML + JS 代码一般都可以预览

这就是你教程里常出现的运行{:4_173:}
这个工具得上置顶,不然哪里找

马黑黑 发表于 2025-5-25 22:00

花飞飞 发表于 2025-5-25 21:59
这个好,刚我试的代码还保存着。直接运行了。。

有记忆功能

马黑黑 发表于 2025-5-25 22:00

花飞飞 发表于 2025-5-25 22:00
这就是你教程里常出现的运行
这个工具得上置顶,不然哪里找

你站里不是有吗

花飞飞 发表于 2025-5-25 22:04

马黑黑 发表于 2025-5-25 22:00
有记忆功能

嗯哪,你的编辑程序都有这个功能。{:4_173:}
只是刚在那个贴子里,突然这个贴子里打开也能看到,就觉得跟时空穿越了一样

花飞飞 发表于 2025-5-25 22:06

马黑黑 发表于 2025-5-25 22:00
你站里不是有吗

嗯哪,好几个天天换着用{:4_173:}

马黑黑 发表于 2025-5-25 22:27

花飞飞 发表于 2025-5-25 22:06
嗯哪,好几个天天换着用

爱用哪个用哪个挺好

马黑黑 发表于 2025-5-25 22:28

花飞飞 发表于 2025-5-25 22:04
嗯哪,你的编辑程序都有这个功能。
只是刚在那个贴子里,突然这个贴子里打开也能看到,就觉得 ...

惊喜来一个

红影 发表于 2025-5-25 22:51

马黑黑 发表于 2025-5-25 22:00
就是得多看个浏览器标签

有个标签也好啊,可以随时打开随时关闭,也方便呢{:4_187:}

梦江南 发表于 2025-5-26 09:01

这个编辑器好哦,测试预览方便了。

马黑黑 发表于 2025-5-26 12:35

梦江南 发表于 2025-5-26 09:01
这个编辑器好哦,测试预览方便了。

{:4_190:}

马黑黑 发表于 2025-5-26 12:37

红影 发表于 2025-5-25 22:51
有个标签也好啊,可以随时打开随时关闭,也方便呢

方便就行

红影 发表于 2025-5-26 15:04

马黑黑 发表于 2025-5-26 12:37
方便就行

嗯嗯,非常方便实用{:4_187:}

花飞飞 发表于 2025-5-26 20:37

马黑黑 发表于 2025-5-25 22:27
爱用哪个用哪个挺好

大框的小框的,彩色的和不彩色的,都齐活了{:4_173:}好用

花飞飞 发表于 2025-5-26 20:38

马黑黑 发表于 2025-5-25 22:28
惊喜来一个

的确,喜从天降点开就有代码

马黑黑 发表于 2025-5-26 21:26

花飞飞 发表于 2025-5-26 20:38
的确,喜从天降点开就有代码

{:4_191:}
页: [1] 2 3 4 5 6
查看完整版本: 简易HTML编辑器