马黑黑 发表于 2023-12-9 09:40

xdEditor初稿源码

本帖最后由 马黑黑 于 2023-12-9 09:48 编辑

一、HTML(xd_editor.html)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<title>xdEditor</title>
<link rel="stylesheet" type="text/css" href="./xd_editor.css">
</head>
<body>

<h2 class="mid">xdEditor</h2>
<form id="xdForm" name="xdForm" action="./save.php" method="post">
      <textarea id="elm" name="content"></textarea>
</form>

<script src="./xd_editor.js"></script>

</body>
</html>

【说明】


(一)上述代码保存为 xd_editor.html 文档作为测试之用,或根据需要将核心代码植入自己的文档中;
(二)form是页面中的第一个form表单,id与name同名,可随意命名。action根据需要设置;
(三)上述代码要求所引用的 xd_editor.css 和 xd_editor.js 与 xd_editor.html 同放在一个目录下,也可根据需要另外存放(需要修改引用路径)。

马黑黑 发表于 2023-12-9 09:42

本帖最后由 马黑黑 于 2023-12-9 09:44 编辑

二、CSS(xd_editor.css)

@charset "utf-8";

a {
      text-decoration: none;
      color: #3f48cc;
}

a:hover, a:visited:hover {
      color: #ff0000;
}

a:visited {
      color: #005344;
}

img {
      border: 0;
}

#ed_outer {
      width: 1024px;
      height: 640px;
      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%;
      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), #insertBoxp:nth-of-type(1) {
      margin: 0 0;
}
.btn {
      width: 22px;
      height: 22px;
      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;
      cursor: pointer;
}

#font_color:hover #forecolor, #bg_color:hover #backcolor {
      opacity: 1;
}

#forecolor, #backcolor {
      width: 100%;
      height: 100%;
      border: none;
      outline: none;
      padding: 0;
      margin: 0;
      opacity: 0;
}

#orderlist {
      width: 20px;
      opacity: 0;
}

#edlist select option:nth-of-type(1) {
      display: none;
}

#sub {
      background: lightblue;
      border-radius: 6px;
      border: 1px solid gray;
      outline: none;
      cursor: pointer;
}

#sub:hover {
      box-shadow: 2px 2px 4px gray;
      color: red;
}

#insertBox {
      position: absolute;
      padding: 6px;
      box-shadow: 2px 2px 4px black;
      background: #fefefe;
      display: none;
}

#insertBox > p { margin: 6px; }

.grow1 {
      flex-grow: 1;
      text-align: right;
}

.mid { text-align: center; }

.right { text-align: right; }

.bold { font-weight: bold; }

【说明】

作为初稿,代码还不够完善,很多地方还需斟酌

马黑黑 发表于 2023-12-9 09:44

三、JS(xd_editor.js)

(function() {

        let xdForm = document.querySelector('form');
        if(!xdForm) return false;
        /* 创建界面 */
        let xdEditor = document.createElement('div');
        let picData = 'data:image/gif;base64,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';
        xdEditor.id = 'ed_outer';
        xdEditor.innerHTML = `
                <div id="ed_top">
                        <button type="button" id="code" class="btn" title="代码模式"></button>
                        <span id="font_color" class="btn" title="字体前景色">
                                <input id="forecolor" class="colorBox" type="color" value="#ff0000" />
                        </span>
                        <span id="bg_color" class="btn" title="字体背景色">
                                <input id="backcolor" class="colorBox" type="color" value="#0000ff" />
                        </span>
                        <button type="button" id="bold" class="btn" title="加粗"></button>
                        <button type="button" id="italic" class="btn"title="斜体"></button>
                        <button type="button" id="underline" class="btn" title="下划线"></button>
                        <button type="button" id="strikethrough" class="btn" title="删除线"></button>
                        <button type="button" id="removeformat" class="btn" title="清除格式"></button>
                        <span id="edlist" class="btn" title="列表">
                                <select id="orderlist">
                                        <option value="0">列表</option>
                                        <option value="insertorderedlist">有序列表</option>
                                        <option value="insertunorderedlist">无序列表</option>
                                </select>
                        </span>
                        <button type="button" id="image" class="btn" title="图片"></button>
                        <button type="button" id="audio" class="btn" title="音频"></button>
                        <button type="button" id="video" class="btn" title="视频"></button>
                        <span class="grow1"><a href="./" title="回退">返回日记</a></span>
                </div>
                <div id="ed_edit">
                        <div id="rDiv" contenteditable="true"><p><br></p></div>
                </div>
                <div id="ed_foot">
                        <span class="grow1"></span>
                        <input type="submit" id="sub" name="sub" value="发布" title="提交" />
                </div>
                <div id="insertBox">
                        <p id="mtitle" class="mid bold">插入</p>
                        <p><label for="oSrc">地址 : </lable><input id="oSrc" type="text" value=""/ size="60" ></p>
                        <p>
                                <span id="s1"><label for="oWidth">宽度 : </lable><input id="oWidth" type="text" value="" size="4" /></span>
                                <span id="s2"><label for="oHeight">高度 : </lable><input id="oHeight" type="text" value="" size="4" /></span>
                                <span id="s3"><label for="oTitle">提示 : </lable><input id="oTitle" type="text" value="" size="8" /></span>
                                <span id="s4"><input id="oCtrl" type="checkbox" value="" /><label for="oCtrl">控制</lable></span>
                                <span id="s5"><input id="oAuto" type="checkbox" value="" /><label for="oAuto">自动</lable></span>
                                <span id="s6"><input id="oLoop" type="checkbox" value="" /><label for="oLoop">循环</lable></span>
                        </p>
                        <p class="right"><input id="btnInsert" type="button" value="确定" /></p>
                </div>
        `;
        xdForm.appendChild(xdEditor); /* div追加到form */
        ed_edit.append(elm); /* textarea加入界面 */
        let codeState = false, insertIdx = 0; /* codeState : 编辑模式;insertIdx : 插入媒体索引 */
        /* btns : 所有按钮;colors : 插入颜色box;exec_btns : 一级执行按钮;media_btns :媒体按钮 */
        let btns = document.querySelectorAll('.btn'),
                colors = document.querySelectorAll('.colorBox');
        let exec_btns = ['bold','italic','underline','strikethrough','removeformat'],
                media_btns = ['image','audio','video'];

        let icon = {bold: 7,italic: 8,underline: 9,strikethrough: 10,font_color: 11,bg_color: 12,removeformat: 14,edlist: 16,image: 22,video: 23,audio: 24,code: 28};
        /* 函数 : 简单格式化代码 */
        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;
        };
        /* 函数 : 工具条按钮组隐藏|显示 */
        let set_btnState = () => {
                btns.forEach((btn,key) => {
                        if(key > 0) btn.style.display = ['inline-block','none'];
                });
        };
        /* 函数 : 隐藏|显示元素,数组传参,用于插入媒体box */
        let hideEle = (hEles,sEles) => {
                hEles.forEach((hEle) => hEle.style.display = 'none');
                sEles.forEach((sEle) => sEle.style.display = 'inline-block');
        };
        /* 工具条按钮上图+功能处理 */
        btns.forEach((item) => {
                let num = icon;
                item.style.background = `url(${picData}) -${num * 20}px 0`;
                item.onclick = () => {
                        if(exec_btns.includes(item.id)) document.execCommand(item.id,false,item.id);
                        if(item.id === 'code') {
                                if(codeState) {
                                        rDiv.innerHTML = elm.value;
                                        elm.style.display = 'none';
                                        rDiv.style.display = 'block';
                                        rDiv.focus();
                                }else{
                                        elm.value = formatCode(rDiv.innerHTML);
                                        elm.style.display = 'block';
                                        elm.focus();
                                        rDiv.style.display = 'none';
                                }
                                codeState = !codeState;
                                item.title = ['代码模式','常规模式'];
                                set_btnState();
                        }
                        if(item.id === 'edlist') {
                                orderlist.onchange = () => document.execCommand(orderlist.value,false,orderlist.value);
                                orderlist.value = "0";
                        }
                        if(media_btns.includes(item.id)) {
                                let hides = [,,];
                                let shows = [,,];
                                let idx = {image: 0, audio: 1, video: 2};
                                insertBox.style.left = item.offsetLeft + 'px';
                                insertBox.style.top = ed_top.clientHeight + 'px';
                                insertBox.style.display = 'block';
                                insertIdx = idx;
                                hideEle(hides,shows);
                                mtitle.innerText = '插入' + ['图片','音频','视频'];
                        }
                        elm.value = formatCode(rDiv.innerHTML);
                };
        });
        /* 插入媒体 */
        btnInsert.onclick = () => {
                rDiv.focus();
                let sWidth = oWidth ? ' wdth="' + oWidth.value + '"': '',
                        sHeight = oHeight ? ' height="' + oWidth.value + '"': '',
                        sAuto = oAuto.checked ? ' autoplay="autoplay"' : '',
                        sLoop = oLoop.checked ? ' loop="loop"' : '',
                        sCtrl = oCtrl.checked ? ' controls="controls"' : '';
                let htmls = [
                        `<img src="${oSrc.value}"${sWidth}${sHeight} alt="" title="${oTitle.value}" />`,
                        `<audio src="${oSrc.value}"${sCtrl}${sAuto}${sLoop}></audio>`,
                        `<video src="${oSrc.value}"${sCtrl}${sWidth}${sHeight}${sAuto}${sLoop}></video>`
                ];
                if(oSrc.value) document.execCommand('inserthtml',false,htmls);
                insertBox.style.display = 'none';
        };
        /* 前景色&背景色 */
        colors.forEach((item,key) => {
                if(!codeState) item.oninput = () => document.execCommand(item.id,false,item.value);
        });
        /* 编辑时同步 */
        rDiv.oninput = () => elm.value = rDiv.innerHTML;
        /* 收起媒体box */
        rDiv.onfocus = elm.onfocus = () => insertBox.style.display = 'none';

})();

【说明】

一口气写成的初稿,也许有许多缺陷与错误,测试尚未发现不能运行。

马黑黑 发表于 2023-12-9 09:51

本帖最后由 马黑黑 于 2023-12-9 09:54 编辑

三楼的第07行是工具条所用图片base64格式的数据,这样就不用操心图片丢失,也可以避免处理图片URL的麻烦。

马黑黑 发表于 2023-12-9 09:54

本帖最后由 马黑黑 于 2023-12-9 09:55 编辑

JS文档虽然构思精巧,代码简洁,逻辑性也基本完好,但感觉很多地方仍需改进。

红影 发表于 2023-12-9 10:23

去试了一下,功能很齐全啊,有文字各种编辑,还能插入音频视频。黑黑厉害{:4_199:}

红影 发表于 2023-12-9 10:28

这个是核心技术啊,比那些做音画的代码高了几个数量级{:4_199:}

红影 发表于 2023-12-9 10:30

马黑黑 发表于 2023-12-9 09:51
三楼的第07行是工具条所用图片base64格式的数据,这样就不用操心图片丢失,也可以避免处理图片URL的麻烦。

可以用这个来保存电脑上的图片么?怎样再恢复成常规的图片格式呢?

红影 发表于 2023-12-9 10:42

我加了首音乐,发现不选择“控制”就看不到,然后删掉再选择带控制的,发现原来的音乐还在播放,变成2个声音了,不知道怎么刷新{:4_173:}

红影 发表于 2023-12-9 10:46

黑黑辛苦了,一口气写成一个能互动的editor编辑软件,太厉害了{:4_199:}

马黑黑 发表于 2023-12-9 10:53

红影 发表于 2023-12-9 10:23
去试了一下,功能很齐全啊,有文字各种编辑,还能插入音频视频。黑黑厉害

这得益于过时的 execCommand 指令,该指令令一切变得简单。w3c将其丢入垃圾桶,大家都觉得不好理解,丢了也没有提供可替代的封装(将来会有)。

要替换掉 execCommand 方法,一个选择是 selection+range ,这会让编程变得相当复杂。selection基于选区,range基于选区范围,理论上也简单,但是,操作DOM的时候,DOM有文本节点、元素节点,例如下句代码:

    <p>这里的<b>山水</b>好迷人</p>

父元素 p 里,有文本节点,有元素节点<b>...</b>。当选择了 “的山”并想给他加颜色 时,就涉及到了文本节点和元素节点跨选区问题,这要处理起来就很费劲:要先判断是否跨节点,然后重新处理节点数据,既要保留原有节点,又要实现加颜色的功能,还要遵守HTML语法规则,等等,工作量是很大的,而 execCommand 将这些要处理的事情都已经封装好,一条命令就可以实现所需功能。

马黑黑 发表于 2023-12-9 10:56

红影 发表于 2023-12-9 10:30
可以用这个来保存电脑上的图片么?怎样再恢复成常规的图片格式呢?

可以用 <img src="..." alt="" /> 标签显示出图片,... 就是那一长串引号里的文本,运行后图片另存为

马黑黑 发表于 2023-12-9 10:58

红影 发表于 2023-12-9 10:42
我加了首音乐,发现不选择“控制”就看不到,然后删掉再选择带控制的,发现原来的音乐还在播放,变成2个声 ...

进入代码模式,将多出的部分删掉再切换回来

马黑黑 发表于 2023-12-9 11:00

红影 发表于 2023-12-9 10:30
可以用这个来保存电脑上的图片么?怎样再恢复成常规的图片格式呢?

<img src="data:image/gif;base64,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" alt="" title="右击另存为">

马黑黑 发表于 2023-12-9 11:01

红影 发表于 2023-12-9 10:46
黑黑辛苦了,一口气写成一个能互动的editor编辑软件,太厉害了

这个比那个demo功能全一点点

马黑黑 发表于 2023-12-9 11:02

红影 发表于 2023-12-9 10:28
这个是核心技术啊,比那些做音画的代码高了几个数量级

功能不同,都是基于CSS+HTML+JS的

红影 发表于 2023-12-9 11:44

马黑黑 发表于 2023-12-9 10:53
这得益于过时的 execCommand 指令,该指令令一切变得简单。w3c将其丢入垃圾桶,大家都觉得不好理解,丢了 ...

这个封装太厉害了,把一切变得简单。
只是从黑黑的话里得出的结论,黑黑谈的这些太专业了,完全不知道的领域{:4_173:}

红影 发表于 2023-12-9 11:50

马黑黑 发表于 2023-12-9 10:56
可以用标签显示出图片,... 就是那一长串引号里的文本,运行后图片另存为

哦,知道了。谢谢黑黑{:4_187:}

红影 发表于 2023-12-9 11:54

马黑黑 发表于 2023-12-9 10:58
进入代码模式,将多出的部分删掉再切换回来

试了一下,可以了{:4_187:}

红影 发表于 2023-12-9 11:57

马黑黑 发表于 2023-12-9 11:00


哇,这些小图全都是base64格式的图图啊{:4_187:}
页: [1] 2 3 4 5 6 7 8 9
查看完整版本: xdEditor初稿源码