马黑黑 发表于 2023-12-6 23:35

极简富文本编辑器Demo

本帖最后由 马黑黑 于 2023-12-6 23:38 编辑 <br /><br /><style>
h2 { text-align: center; }
#ed_outer {
        width: 740px;
        height: 500px;
        border-radius: 6px;
        box-shadow: 3px 6px 12px darkgray;
        margin: 20px auto;
        position: relative;
        display: flex;
        flex-direction: column;
}
#ed_top, #ed_foot {
        border: 1px solid gray;
        border-radius: 6px;
        background: #f0f0f0;
        padding: 8px;
        display: flex;
        gap: 0 4px;
        align-items: center;
}
#ed_top {
        border-radius:6px 6px 0 0;
        border-bottom-color: transparent;
}
#ed_edit {
        flex-grow: 1;
        border: 1px solid gray;
        box-sizing: border-box;
        position: relative;
}
#ed_foot {
        border-radius: 0 0 6px 6px;
        border-top-color: transparent;
}
#elm, #rDiv {
        width: 100%;
        height: 100%;
        background: white;
        padding: 8px;
        border: none;
        outline: none;
        overflow: hidden auto;
        resize: none;
        box-sizing: border-box;
        word-break: break-word;
        tab-size: 4;
        position: absolute;
}
#elm {
        font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
        background: #f8f8dc;
        display: none;
}
#rDiv > p {
        margin: 8px 0;
        padding: 0;
}
#rDiv > p:nth-of-type(1)) { margin: 0 0; }
.btn {
        width: 30px;
        height: 30px;
        border: none;
        outline: none;
        padding: 0;
        cursor: pointer;
}
.btn:hover { border: 1px solid gray; }

#font_color, #bg_color, #edlist {
        width: 20px;
        height: 20px;
        box-sizing: border-box;
        display: flex;
        align-items: center;
}
.grow1 {
        flex-grow: 1;
        text-align: right;
}
</style>

<h2>mhEditor</h2>
<div id="ed_outer">
        <div id="ed_top">
                <button id="code" class="btn" title="代码模式">&lt;/&gt;</button>
                <button id="bold" class="btn" title="加粗(Ctrl+B)"><b>B</b></button>
                <button id="italic" class="btn" title="斜体(Ctrl+I)"><i>I</i></button>
                <button id="underline" class="btn" title="下划线(Ctrl+U)"><u>U</u></button>
                <button id="strikethrough" class="btn" title='删除线'><del>D</del></button>
                <button id="removeformat" class="btn" title="清除格式">C</button>
                <span class="grow1"><a href="http://mhh.52qingyin.cn/">整站系统</a></span>
        </div>
        <div id="ed_edit">
                <textarea id="elm">Textarea</textarea>
                <div id="rDiv" contenteditable="true"><p><br></p></div>
        </div>
        <div id="ed_foot">
                <span class="grow1"></span>
                <button id="test" onclick='alert("感谢点击!")'>假装发布</button>
        </div>
</div>

<script>

(function() {
        let codeState = false;
        let btns = document.querySelectorAll('.btn');

        let formatCode = (code) => {
                let regAr = [
                        [/(<\/p>|<\/div>)(<)/g, '$1\n$2'],
                        [/<div>(<br>)?<\/div>|<p><\/p>/g, ''],
                        [/^[\t]*\n/gm, '']
                ];
                regAr.forEach((item) => {
                        code = code.replaceAll(item,item);
                });
                return code;
        };

        btns.forEach((item) => {
                item.onclick = () => {
                        if(item.id === 'code') {
                                if(codeState) {
                                        rDiv.innerHTML = elm.value;
                                        elm.style.display = 'none';
                                        rDiv.style.display = 'block';
                                }else{
                                        elm.value = formatCode(rDiv.innerHTML);
                                        elm.style.display = 'block';
                                        rDiv.style.display = 'none';
                                }
                                codeState = !codeState;
                        }else{
                                document.execCommand(item.id,false,item.id);
                        }
                        elm.value = formatCode(rDiv.innerHTML);
                };
        });

        rDiv.oninput = () => elm.value = rDiv.innerHTML;
})();

</script>

马黑黑 发表于 2023-12-6 23:38

代码
<style>
h2 { text-align: center; }
#ed_outer {
        width: 740px;
        height: 500px;
        border-radius: 6px;
        box-shadow: 3px 6px 12px darkgray;
        margin: 20px auto;
        position: relative;
        display: flex;
        flex-direction: column;
}
#ed_top, #ed_foot {
        border: 1px solid gray;
        border-radius: 6px;
        background: #f0f0f0;
        padding: 8px;
        display: flex;
        gap: 0 4px;
        align-items: center;
}
#ed_top {
        border-radius:6px 6px 0 0;
        border-bottom-color: transparent;
}
#ed_edit {
        flex-grow: 1;
        border: 1px solid gray;
        box-sizing: border-box;
        position: relative;
}
#ed_foot {
        border-radius: 0 0 6px 6px;
        border-top-color: transparent;
}
#elm, #rDiv {
        width: 100%;
        height: 100%;
        background: white;
        padding: 8px;
        border: none;
        outline: none;
        overflow: hidden auto;
        resize: none;
        box-sizing: border-box;
        word-break: break-word;
        tab-size: 4;
        position: absolute;
}
#elm {
        font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
        background: #f8f8dc;
        display: none;
}
#rDiv > p {
        margin: 8px 0;
        padding: 0;
}
#rDiv > p:nth-of-type(1)) { margin: 0 0; }
.btn {
        width: 30px;
        height: 30px;
        border: none;
        outline: none;
        padding: 0;
        cursor: pointer;
}
.btn:hover { border: 1px solid gray; }

#font_color, #bg_color, #edlist {
        width: 20px;
        height: 20px;
        box-sizing: border-box;
        display: flex;
        align-items: center;
}
.grow1 {
        flex-grow: 1;
        text-align: right;
}
</style>

<h2>mhEditor</h2>
<div id="ed_outer">
        <div id="ed_top">
                <button id="code" class="btn" title="代码模式"></></button>
                <button id="bold" class="btn" title="加粗(Ctrl+B)"><b>B</b></button>
                <button id="italic" class="btn" title="斜体(Ctrl+I)"><i>I</i></button>
                <button id="underline" class="btn" title="下划线(Ctrl+U)"><u>U</u></button>
                <button id="strikethrough" class="btn" title='删除线'><del>D</del></button>
                <button id="removeformat" class="btn" title="清除格式">C</button>
                <span class="grow1"><a href="http://mhh.52qingyin.cn/">整站系统</a></span>
        </div>
        <div id="ed_edit">
                <textarea id="elm">Textarea</textarea>
                <div id="rDiv" contenteditable="true"><p><br></p></div>
        </div>
        <div id="ed_foot">
                <span class="grow1"></span>
                <button id="test" onclick='alert("感谢点击!")'>假装发布</button>
        </div>
</div>

<script>

(function() {
        let codeState = false;
        let btns = document.querySelectorAll('.btn');

        let formatCode = (code) => {
                let regAr = [
                        [/(<\/p>|<\/div>)(<)/g, '$1\n$2'],
                        [/<div>(<br>)?<\/div>|<p><\/p>/g, ''],
                        [/^[\t]*\n/gm, '']
                ];
                regAr.forEach((item) => {
                        code = code.replaceAll(item,item);
                });
                return code;
        };

        btns.forEach((item) => {
                item.onclick = () => {
                        if(item.id === 'code') {
                                if(codeState) {
                                        rDiv.innerHTML = elm.value;
                                        elm.style.display = 'none';
                                        rDiv.style.display = 'block';
                                }else{
                                        elm.value = formatCode(rDiv.innerHTML);
                                        elm.style.display = 'block';
                                        rDiv.style.display = 'none';
                                }
                                codeState = !codeState;
                        }else{
                                document.execCommand(item.id,false,item.id);
                        }
                        elm.value = formatCode(rDiv.innerHTML);
                };
        });

        rDiv.oninput = () => elm.value = rDiv.innerHTML;
})();

</script>


马黑黑 发表于 2023-12-6 23:56

Wndows 的记事本,是一个处理纯文本的编辑器,Windows还有一个富文本编辑器,在安装word之类的办公软件之前编辑富文本的极好替代品,用过的朋友应该知道什么叫做富文本。

富文本编辑器支持图文共存。Web富文本编辑器,可以使用div之类的常规元素来模拟编辑界面,仅需一个属性值设置,contenteditable="true"。当 contenteditable 设置为true,div 或 p 之类的元素,就都可以对之编辑,且支持粘贴图片、给选定文本设置样式等。HTML核心代码举例如下:

<div contenteditable="true"></div>

上面这句代码,就可以令 div 处于可编辑状态。当然,我们需要给它设置样式,并用JS处理一些输入交互,正如本帖示例一样。

Demo只是一个简单展示,它提供一个基础,我们可以在此基础上进行扩展。

特别说明:本示例工具栏的几个小功能,均使用 execCommand 实现,该API已经被w3c宣布即将下架,但各大浏览器仍然完好支持。

马黑黑 发表于 2023-12-7 00:05

contenteditable 属性是所有现代浏览器都支持的。contenteditable的值有三个:

true : 元素进入可编辑其内内容
false : 元素不可以编辑内容
plaintext-only : 元素仅可编辑纯文本,就像textArea控件一样,遗憾的是目前只有Chromium类的浏览器支持

除了 contenteditable 属性,还可以通过 CSS 的 user-modify 属性设置元素实现,仅 Chromium 类浏览器支持。

马黑黑 发表于 2023-12-7 08:21

本帖最后由 马黑黑 于 2023-12-7 08:23 编辑

作为demo,示例是不完善的,也自然会存在逻辑上的以及其他方面的缺陷。也许,我这里只是提供一种思路,虽然主要是想展现HTML之强大。

如果要做一个可以部署到自己的项目的编辑器,demo远远不够,需要进一步构思与完善。此外要指出的是,特别重要的:

要有 form,上述代码全部或部分隶属于form。当 button 置于form中,要给出 type 属性,type="button",否则每一个按钮都是提交。真正的提交按钮 type="submit“。form和提交按钮都应有 name 属性,然后结合项目,form的提交行为即 action 属性指向根据需要给出明确值。

幸运草 发表于 2023-12-7 09:05

富文本,介个名字好听{:6_228:}

幸运草 发表于 2023-12-7 09:06

虽然不懂,看懂 mhEditor 的含义了,就是马黑编辑器{:6_228:}

红影 发表于 2023-12-7 09:13

输入一些文字,果然可以做加粗斜体等操作。还是头一次知道这个,感谢黑黑的介绍{:4_199:}

红影 发表于 2023-12-7 09:31

图片也可以放进去呢,只是不知道怎么去调。也不错,点代码模式,就直接看到图片地址了{:4_173:}

亦是金 发表于 2023-12-7 09:40

太深奥,看不懂。按红影说的试了试,挺灵的。我就跟在大侠后面学习套用!{:4_203:}

马黑黑 发表于 2023-12-7 12:43

亦是金 发表于 2023-12-7 09:40
太深奥,看不懂。按红影说的试了试,挺灵的。我就跟在大侠后面学习套用!

这只是一个demo编辑器代码,演示一下

马黑黑 发表于 2023-12-7 12:46

幸运草 发表于 2023-12-7 09:05
富文本,介个名字好听

{:4_172:}

马黑黑 发表于 2023-12-7 12:47

幸运草 发表于 2023-12-7 09:06
虽然不懂,看懂 mhEditor 的含义了,就是马黑编辑器

{:4_181:}

马黑黑 发表于 2023-12-7 12:47

红影 发表于 2023-12-7 09:31
图片也可以放进去呢,只是不知道怎么去调。也不错,点代码模式,就直接看到图片地址了

富文本,就是不止是文本

马黑黑 发表于 2023-12-7 12:48

红影 发表于 2023-12-7 09:31
图片也可以放进去呢,只是不知道怎么去调。也不错,点代码模式,就直接看到图片地址了

这个不是全功能编辑器,可以在代码模式中调整

马黑黑 发表于 2023-12-7 12:53

红影 发表于 2023-12-7 09:13
输入一些文字,果然可以做加粗斜体等操作。还是头一次知道这个,感谢黑黑的介绍

论坛发帖都有这类编辑器的。富文本编辑器的开发门槛高、坑多且深,据说很多人都钻进去后出不来。

如果仅仅基于 contenteditable+execCommand,做个轻奢型的富文本编辑器其实不难,问题是,execCommand已经判了死刑,我们得基于 contenteditable + (range + section)去开发,或者用数据模型+映射方式来实现,这将是一个庞大复杂的工程。

红影 发表于 2023-12-7 13:06

马黑黑 发表于 2023-12-7 12:47
富文本,就是不止是文本

原来是这个意思啊。

红影 发表于 2023-12-7 13:06

马黑黑 发表于 2023-12-7 12:48
这个不是全功能编辑器,可以在代码模式中调整

哦哦,我试试。。。

马黑黑 发表于 2023-12-7 13:07

红影 发表于 2023-12-7 13:06
哦哦,我试试。。。

{:4_190:}

马黑黑 发表于 2023-12-7 13:07

红影 发表于 2023-12-7 13:06
原来是这个意思啊。

只是文本,是穷文本,记事本就是这个。
页: [1] 2 3 4
查看完整版本: 极简富文本编辑器Demo