Unable to use inside Shadow Dom and Webcomponents
See original GitHub issueWe wanted to use EditorJS with customElements (we use lit-html
internally). Our reasons behind this is because we wanted to isolate the editor styles away from the main dom so that our CMS styles don’t make the editor content look strange. This way we can also customize the CSS nicely so that the end user looks at the formatting and text with about the same styles as they would on the frontend.
We previously used a Iframe for this (which still works), but we would like to avoid that if possible.
With the editor in the customElement, we get errors like these:
Here is the code that creates the customElement:
import '@editorjs/editorjs/dist/editor.js';
import {LitElement, html, css} from 'lit-element';
class HTMLEngine {
constructor(inputEl, templateSelector, editorConfig) {
this.inputEl = inputEl;
this.editorConfig = editorConfig;
}
read() {
let doc = new DOMParser().parseFromString(this.inputEl.value, 'text/html');
var dataTemplate = doc.querySelector('[data-editor-data]');
if (dataTemplate) {
var jsonData = dataTemplate.dataset.editorData;
return JSON.parse(jsonData);
}
return null;
}
write(outputData) {
var editorData = JSON.stringify(outputData);
var renderedBlocks = '';
outputData.blocks.forEach((block) => {
renderedBlocks += this.renderBlock(block);
});
this.inputEl.value = `<template data-editor-data='${editorData}'></template>
${renderedBlocks}`;
}
renderBlock(block) {
return this.editorConfig.tools[block.type].HTMLGenerator(block.data) + '\n';
}
}
class OstinatoEditorWidget extends LitElement {
static get properties() {
return {
saveTo: { type: String },
editorConfig: { type: String },
editorFramePath: { type: String },
}
}
constructor() {
super();
this.saveTo = '';
this.editorConfig = '';
this.editorFramePath = '';
}
connectedCallback() {
super.connectedCallback();
this.saveToEl = document.querySelector(this.saveTo);
// Now import our editor config.
import(this.editorConfig).then((m) => {
this.config = m.editorConfig;
this.engine = new HTMLEngine(
this.saveToEl,
'[editorjs-data]',
this.config);
this.initEditor();
});
}
initEditor() {
this.config.data = this.engine.read();
this.config.holder = this.shadowRoot.getElementById('editor');
this.config.onChange = function() {
this.editor.save().then((outputData) => {
if (outputData) this.engine.write(outputData);
});
}.bind(this);
console.log(this.config);
this.editor = new EditorJS(this.config);
}
static get styles() {
return css`
:host {
display: block;
width: 100%;
}
#editor {
width: 100%;
box-shadow: 0 0 1px 2px #e3e3e3;
}
`;
}
render() {
return html`
<div id="editor"></div>
`;
}
}
customElements.define('ostinato-editor-widget', OstinatoEditorWidget);
And this is how we are using the element:
{% load staticfiles %}
<textarea name="{{ widget.name }}"
{% include "django/forms/widgets/attrs.html" %}
style="display: none;">{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
<ostinato-editor-widget
saveTo='[name="{{ widget.name }}"]'
editorConfig="{{ config_module }}">
</ostinato-editor-widget>
Edit: Oh and here is the config that is used.
export const editorConfig = {
initialBlock: "paragraph",
minHeight: "400px",
autoFocus: true,
tools: {
header: {
class: Header,
config: {
placeholder: 'Header Text'
},
shortcut: 'CMD+SHIFT+H',
HTMLGenerator: (data) => `<h${data.level}>${data.text}</h${data.level}>`,
},
paragraph: {
class: Paragraph,
shortcut: 'CMD+SHIFT+P',
HTMLGenerator: (data) => `<p>${data.text}</p>`,
},
list: {
class: List,
inlineToolbar: true,
shortcut: 'CMD+SHIFT+L',
HTMLGenerator: (data) => {
let tagname = data.style.charAt(0) + 'l';
var renderItem = (item) => { return `<li>${item}</li>`; }
var items = '';
data.items.forEach((item) => { items += renderItem(item); });
return `<${tagname}>${items}</${tagname}>`;
},
},
quote: {
class: Quote,
inlineToolbar: true,
config: {
quotePlaceholder: 'Enter a quote',
captionPlaceholder: 'Caption or Author',
},
shortcut: 'CMD+SHIFT+O',
HTMLGenerator: (data) => {
return `<blockquote style="quote-${data.alignment}">
<p class="quote-text">${data.text}</p>
<p class="quote-caption">${data.caption}</p>
</blockquote>`;
},
},
warning: {
class: Warning,
inlineToolbar: true,
shortcut: 'CMD+SHIFT+W',
config: {
titlePlaceholder: 'Warning Title',
messagePlaceholder: 'Warning Message',
},
HTMLGenerator: (data) => {
return `<div class="warning">
<p class="warning-title">${data.title}</p>
<p class="warning-message">${data.message}</p>
</div>`;
},
},
marker: {
class: Marker,
shortcut: 'CMD+SHIFT+M'
},
code: {
class: CodeTool,
shortcut: 'CMD+SHIFT+C',
HTMLGenerator: (data) => { return `<code>${data.code}</code>`; }
},
delimiter: {
class: Delimiter,
HTMLGenerator: () => { return `<div class="ce-delimiter"></div>` }
},
inlineCode: {
class: InlineCode,
shortcut: 'CMD+SHIFT+C'
},
table: {
class: Table,
inlineToolbar: true,
shortcut: 'CMD+ALT+T',
HTMLGenerator: (data) => {
var rows = '';
data.content.forEach((row) => {
var cells = '';
row.forEach((cell) => { cells += '<td>' + cell + '</td>'; })
rows += '<tr>' + cells + '</tr>';
});
return '<table>' + rows + '</table>';
}
},
},
};
Issue Analytics
- State:
- Created 4 years ago
- Reactions:5
- Comments:13 (2 by maintainers)
Top Results From Across the Web
Unable to use inside Shadow Dom and Webcomponents #1881
I found an easy workaround to be able to use editorJS inside a web component. When designing your web component with your favorite...
Read more >Using shadow DOM - Web Components | MDN
Using shadow DOM. An important aspect of web components is encapsulation — being able to keep the markup structure, style, and behavior ...
Read more >Not able use Bootstrap within Shadow Root of Custom Elements
I am new to web components and shadow root. I am not sure whether using bootstrap inside the shadow root is good approach...
Read more >Working With Web Components (Shadow DOM) - HeadSpin
Web Components help encapsulate styling and functionality for particular components, enabling sharing and reusing them within and across ...
Read more >Shadow DOM v1 - Self-Contained Web Components
Shadow DOM composes different DOM trees together using the <slot> element. Slots are placeholders inside your component that users can fill with ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Any update on this?
Btw, it’s specially annoying that plugins doesn’t use any encapsulation nor namespacing for class names, so it’s very easy to mess up. Now that Shadow Dom is widely supported across all major browsers, it would be awesome to embrace it for editor.js internals as well!
@gohabereg ok so it seems id didn’t just have anything to do with the tools. I attempted to use it without specifying any tools in my config, just to test. And the editor still doesn’t load.