
今天写项目的时候,遇到一个问题,分享给大家。
我有一个A区域,还有一个B区域。A区域内的Vue组件可以通过Vuedraggable这个框架来拖拽到B区域中。B区域内的Vue组件在标题上使用了element-tiptap组件(用来高级编辑)。然而如果B区与内存在使用了element-tiptap组件的组件。就会出现把 A区域中组件的文本内容拖拽到element-tiptap中。
其实我想要的创建新的组件,而它把我拖拽的组件的文本内容添加到了组件中的element-tiptap中。
HTML元素设置了contenteditable="true"之后,或者input输入框之间,默认都是可以把选中的文字拖拽到其他的input的元素或者contenteditable="true"的元素中。
我要做的就是禁止其他页面元素把文本内容拖拽到element-tiptap的组件中,element-tiptap组件的原理就是用的contenteditable="true"来实现高级编辑。
我自己写了一个组件案例,用来验证是否contenteditable="true"的情况下,能通过重写drop事件禁止拖拽:
自定义组件:DragDiv.vue
姐姐,为什么要告诉这楚枫你的身份?风铃对里雾问道他今日既然帮了忙,之前的恩怨,自然要化解。与其日后被他发现,还不如我先告诉他。里雾说道我记得你说过他,但你当时不是说,他是你在祖武天河,遇到的小角色吗?那为何这次遇到,你却又突然告知于我,他可以帮我破阵呢?风铃对里雾问道她与楚枫第一次见面,便是在那行宫之内,但其实在她见到楚枫之前就已经收到了里雾的通知告关于楚枫要去那行宫,以及楚枫有些特别,风铃可以尝试利用楚枫破解行宫考验等事刚好诅咒之力发作,楚枫出手相助,虽然只是化解了表面症状,但能做到这一点已经很不简单。
人生何处不相逢
实际是生效的,可以通过这种方式来解决这个问题。所以我应该找的是drop事件相关的配置。这里我想了很多方法:
1. 查找element-tiptap 官方文档和源代码,看看有没有相关的组件配置,用来防止其他页面元素拖拽文件进来。onDrop方法前后打印当前的值,结果是相同的,无法解决当前问题。
2. csdn站内包括其他网站的解决方案:把HTML元素的draggable="false",我加上试了没有生效。并没有修改drop事件的处理函数。element-tiptap中自定义了drop事件的处理函数。
3. 实在没办法,只能通过浏览器Debug去分析它的加载流程,包括事件的处理顺序,找到了element-tiptap中,设置contenteditable="true"的div,类名是:ProseMirror,它上面加了两个drop事件的处理函数,对应到它框架代码:
editHandlers.drop = (view, _event) => {let event = _event;let dragging = view.dragging;view.dragging = null;if (!event.dataTransfer)return;let eventPos = view.posAtCoords(eventCoords(event));if (!eventPos)return;let $mouse = view.state.doc.resolve(eventPos.pos);let slice = dragging && dragging.slice;if (slice) {view.someProp("transformPasted", f => { slice = f(slice); });}else {slice = parseFromClipboard(view, event.dataTransfer.getData(brokenClipboardAPI ? "Text" : "text/plain"), brokenClipboardAPI ? null : event.dataTransfer.getData("text/html"), false, $mouse);}let move = !!(dragging && !event[dragCopyModifier]);if (view.someProp("handleDrop", f => f(view, event, slice || Slice.empty, move))) {event.preventDefault();return;}if (!slice)return;event.preventDefault();let insertPos = slice ? dropPoint(view.state.doc, $mouse.pos, slice) : $mouse.pos;if (insertPos == null)insertPos = $mouse.pos;let tr = view.state.tr;if (move)tr.deleteSelection();let pos = tr.mapping.map(insertPos);let isNode = slice.openStart == 0 && slice.openEnd == 0 && slice.content.childCount == 1;let beforeInsert = tr.doc;if (isNode)tr.replaceRangeWith(pos, pos, slice.content.firstChild);elsetr.replaceRange(pos, pos, slice);if (tr.doc.eq(beforeInsert))return;let $pos = tr.doc.resolve(pos);if (isNode && NodeSelection.isSelectable(slice.content.firstChild) &&$pos.nodeAfter && $pos.nodeAfter.sameMarkup(slice.content.firstChild)) {tr.setSelection(new NodeSelection($pos));}else {let end = tr.mapping.map(insertPos);tr.mapping.maps[tr.mapping.maps.length - 1].forEach((_from, _to, _newFrom, newTo) => end = newTo);tr.setSelection(selectionBetween(view, $pos, tr.doc.resolve(end)));}view.focus();view.dispatch(tr.setMeta("uiEvent", "drop"));
};
这里可以看到它的处理流程还是很复杂,我的需求里面没有拖拽文本到B区域中组件的需求,所以这个事件处理函数可以在它的框架代码中删除。
但是项目下有很多的依赖,暂时只能通过如下方法来解决这个问题:(跳过element-tiptap中的drop事件处理函数)
prosemirror-view/dist/index.js:3403行下加入return;
1. 前端框架的很多高级特性都基于HTML中不常见的属性来实现,在使用框架过程中,应该多去看看它的实现原理,分析关键代码。
2. 编写简洁的代码。
3. 虽然是前端的框架,但是现在很多框架都是用TypeScript来实现,也是有OOP的实践在里面,要多考虑对象的行为和特性,不能把对象属性都暴露,也不能都不暴露。
4. 复用的可能,如果一个框架中组件的复用度不高,引入这个框架,对于项目的后期维护非常不利,想弃用,有依赖 。想重写,需要兼容。
参考:
GitHub - Leecason/element-tiptap: 🌸A modern WYSIWYG rich-text editor using tiptap and Element UI for Vue2 (🚀 tiptap2 and Vue3 is in alpha)
https://github.com/ueberdosis/tiptap
draggable - HTML(超文本标记语言) | MDN
contenteditable - HTML(超文本标记语言) | MDN
欢迎大家私信,留言学习交流。
下一篇:微服务外交官-Feign