我正在尝试构建一个自定义的TinyMCE编辑器来教授内容,这些内容允许某些块被包装为“活动”。内容块中将有多个活动,因此他们将ID作为主键等。
我的挑战是实现一个允许这样做的插件 - 理想情况下,我会使用短代码,但它们非常容易出错。我正在研究使用通过Polymer渲染的自定义HTML标签 - 可以这样做吗?
答案 0 :(得分:0)
我在大约4个小时后完成了它。
需要对TinyMCE编辑器进行初始化,并支持自定义元素:
{
...
extended_valid_elements : 'module-activity',
custom_elements : 'module-activity',
init_instance_callback: function(editor) {
registerCustomWebComponents(tinymce.activeEditor.dom.doc);
},
...
}
registerCustomWebComponents
的样子:
function registerCustomWebComponents(doc) {
doc.registerElement('module-activity', ModuleActivityHTMLElement);
}
我最终定义了自定义HTML元素,然后定义了React组件,而不是将HTMl构建为字符串。
class ModuleActivity extends React.Component {
constructor(props) {
super(props);
this.openActivityEdit = this.openActivityEdit.bind(this);
}
openActivityEdit() {
}
render() {
return <div>
<h3>Module Activity</h3>
<button onClick={this.openActivityEdit}>Edit</button>
<div dangerouslySetInnerHTML={{__html: this.props.contentHtml }} />
</div>;
}
}
class ModuleActivityHTMLElement extends HTMLElement {
attachedCallback() {
let self = this;
var mountPoint = document.createElement('div');
this.createShadowRoot().appendChild(mountPoint);
ReactDOM.render(<ModuleActivity contentHtml={self.innerHTML}/>, mountPoint);
}
}
答案 1 :(得分:0)
这里的答案是不要在聚合物中使用tinymce,tinymce严重依赖于文档的根,而dom会破坏它。
但是就像生活中的一切一样,一切都不会丢失...
在聚合物模板中使用一个对象,使该对象加载tinymce并解决文档根目录问题。然后,您就可以通过这种方式从对象加载后访问tinymce。
创建一个HTML文件以加载tinymce
<!DOCTYPE html>
<html>
<head>
<script src="https://cloud.tinymce.com/stable/tinymce.min.js"></script>
<style>
html { height: 96%; }
body { height: 96%; }
</style>
</head>
<body>
<textarea>Loading...</textarea>
<script>
var ta = document.querySelector('textarea');
ta.tinymce = tinymce;
var editorChangeHandler;
tinymce.init({ selector: 'textarea', height: document.body.scrollHeight - 100, setup: function (editor) {
editor.on('Paste Change input Undo Redo', function () {
if (editorChangeHandler) { clearTimeout(editorChangeHandler); }
editorChangeHandler = setTimeout(function () {
ta.dispatchEvent(new CustomEvent('save'));
}, 2000);
});
} });
</script>
</body>
</html>
现在,您只需要将一个对象添加到组件的模板中,即可使用对象的data属性加载html。
一旦加载,就可以访问它,查询对象dom并获取文本区域,添加事件侦听器以保存自定义事件,还可以预先设置内容,调整高度等。我只测试了相同的域,因此请注意,这可能会破坏跨域,但是无论如何您都希望与其他组件一起使用。
将对象添加到组件模板
<object id="editor" type="text/html" data="/src/lib/tinymce/tinymce.html"></object>
以某种方式进行预加载,抓取物品,设置高度并进行保存
ready() {
super.ready();
// wait five seconds before capturing input
var interval = setInterval(() => {
if (!this.$.editor.contentDocument.body) return;
let ta = this.$.editor.contentDocument.body.querySelector('textarea');
if (!ta || !ta.tinymce || !ta.tinymce.activeEditor) return;
// clear interval now loaded
window.clearInterval(interval);
setTimeout(() => {
// resize on window change
window.addEventListener('resize', this._updateEditorSize.bind(this));
// pre load
ta.tinymce.activeEditor.setContent(this.element.value);
// catch updates every few seconds, this will then have a 4 second debounce on save too naturally
ta.addEventListener('save', (ev) => {
this.set('element.value', ta.tinymce.activeEditor.getContent({ format: 'raw' }));
});
}, 250);
}, 250);
}
这是适用于聚合物3和tinymce的工作方案,可以快速加载,自动调整大小并捕获对象的保存事件,而我不必从默认设置中更改tinymce。您还可以将这种方法用于其他方法,以避开某些嵌入式应用程序的影子dom。
答案 2 :(得分:0)
@liamzebedee提供的解决方案有些过时,这是一个带有ES6模块和标准Web组件的解决方案。
与上面的代码的主要区别是在TinyMCE <iframe/>
中注入了定义Web组件的脚本。否则,可接受的标签将保持惰性。
首先,这是与TinyMCE初始化相关的代码:
const name = 'custom-element';
const attribute = 'view-mode';
const insertTag = '<custom-element view-mode="editing"></custom-element>';
const definitionFile = './custom-element.js';
tinymce.PluginManager.add(name, function(editor, url) {
editor.ui.registry.addButton(name, {
text: name,
onAction: () => editor.insertContent(insertTag)
});
});
tinymce.init({
// ...
custom_elements: name, // just the custom Web element names
extended_valid_elements: `${name}[${attribute}]`, // names+attributes
init_instance_callback: function(editor) {
const edDoc = editor.getDoc();
const scriptTag = edDoc.createElement('script');
scriptTag.src = definitionFile;
scriptTag.type = 'module’;
// Injection of the definition file in the <iframe/> document
edDoc.querySelector('head').appendChild(scriptTag);
}
});
相应的(最小)Web组件定义为:
export class SimpleNumber extends HTMLElement {
constructor() {
super();
// Add a simple <input/> field in the ShadowRoot
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
:host { display:inline-block; }
input { border: 1px solid blue; font-size: 12pt; text-align: right; }
</style>
<input type="number" min="0" max="10" value="0" />
`;
// Provide some feedback when the <input/> field value changes
this.shadowRoot.querySelector('input').addEventListener('change', (event) => {
const field = event.target;
field.style.backgroundColor = field.value === '3' ? 'lightgreen' : 'orange';
});
}
}
if (window.customElements && !customElements.get('simple-number')){
customElements.define('simple-number', SimpleNumber);
}
请注意,我无法在 codepen.io 上提供正在运行的示例(例如),因为我无法为自定义元素定义提供单独的JS文件(该文件需要插入{ {1}})…