如何使用 Ace Editor 创建自定义 HTML 元素?

How to create a custom HTML element with Ace Editor?

我正在尝试使用将包含 Ace 编辑器实例的 Web 组件创建自定义 HTML 元素。我尝试的解决方案如下

 customElements.define("test-editor", class  extends HTMLElement {
      constructor() {
           super();

           const editorContainer = document.createElement("div");
           editorContainer.setAttribute("id", "editor_container");

           const root = this.attachShadow({mode: "open"});
           root.appendChild(editorContainer);
      }

      connectedCallback() {
           ace.edit("editor_container");
      }
 });

然后我尝试在下面HTML中使用这个自定义元素(custom_element.js包含上面的内容)

 <!DOCTYPE html>
 <html lang="en">
 <head>
      <meta charset="UTF-8">
      <style>
           html, body, #editor_container {
                width: 100%;
                height: 100%;
           }
      </style>
 </head>
 <body>
      <test-editor></test-editor>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script>
      <script src="custom_element.js"></script>
 </body>
 </html>

当我在 Chrome 或 Firefox 中加载此页面时,我在控制台中收到以下错误

Uncaught Error: ace.edit can't find div #editor_container
at Object.t.edit (ace.js:1)
at HTMLElement.connectedCallback (custom_element.js:17)
at custom_element.js:3

有什么方法可以在自定义元素中嵌入 Ace Editor 的实例吗?

您已将阴影DOM附加到您的组件。

因为ACE Editor代码在页面中加载DOM(绿色),
它只能在页面 DOM(绿色)中找到 ACE 容器。

注意:您在第 DOM 页中也有 <style>。这永远不会为元素 in shadowDOM.
设置样式 shadowDOM整个本质就是封装

参见:https://developers.google.com/web/fundamentals/web-components/shadowdom

您有 2 个选项可以使 ACE 编辑器在自定义元素中工作:

<!DOCTYPE html>
 <html lang="en">
   <head>
     <meta charset="UTF-8">
     <script id=myEditor>
       customElements.define("my-editor", class extends HTMLElement {
         connectedCallback() {
           const script = document.createElement("SCRIPT");
           script.src = "//cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js";
           script.onload = () => {
             this.innerHTML = `<div id=editor_container>${myEditor.innerHTML}</div>`;
             ace.edit("editor_container", {
               mode: "ace/mode/javascript",
               theme: "ace/theme/cobalt"
             });
           }
           document.head.append(script);
         }
       });
     </script>
     <style>
       html,
       body,
       #editor_container {
         width: 100%;
         height: 100%;
       }
     </style>
   </head>
   <body>
     <my-editor></my-editor>
   </body>
 </html>

或在以下位置查看更多配置示例: https://jsfiddle.net/CustomElementsExamples/p28mzu7g/

ace 存储库中有 a demo 使用 shadow-dom

重要的部分是将 dom 元素而不是字符串传递给编辑方法 并致电 editor.renderer.attachToShadowRoot

如果您正在寻找将编辑器添加到 Web 组件的最简单方法,Ace 团队确实提供了 api,只需构建一个编辑器,然后将其添加到影子 dom,例如这个:

aceEditor.renderer.attachToShadowRoot();

这是我为显示文件中的一段代码而构建的(它显示文件名和代码):

class CodeSnippet extends HTMLElement {
  constructor(fileName, fileContents) {
    super();

    this._fileName = fileName;
    this._fileContents = fileContents;

    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(this.getTemplate());
  }

  getTemplate() {
    const template = document.createElement('template');
    template.innerHTML = `
<style>
  .fileName {
    color: gray;
  }
</style>
<div class="fileName"></div>
<div class="codeSnippet"></div>`;
    return template.content.cloneNode(true);
  }

  connectedCallback() {
    //display the file name
    const fileName = this.shadowRoot.querySelector('.fileName');
    fileName.innerHTML = this._fileName;

    //used to set the height of the ace editor (always have at least one line)
    let numNewLines = 1;
    //collect the newlines (null if there are none)
    const allNewLines = this._fileContents.match(/\n/g);
    if(allNewLines) {
      //add an extra for the last line without a newline on it
      numNewLines = allNewLines.length + 1;
    } 

    //get the element where you want to add the editor
    const codeSnippet = this.shadowRoot.querySelector('.codeSnippet');
    //build an ace editor like normal
    const aceEditor = ace.edit(codeSnippet, {
        theme: "ace/theme/monokai",
        mode: "ace/mode/java",
        value: this._fileContents, //code to display
        showPrintMargin: false,
        readOnly: true,
        fontSize: 16,
        maxLines: numNewLines, //auto set the height of the editor
        highlightActiveLine: false,
        highlightGutterLine: false
    });

    //attach the ace editor to the shadow dom
    aceEditor.renderer.attachToShadowRoot();
  } 
}

window.customElements.define('code-snippet', CodeSnippet);

不要忘记您需要在代码中的某处访问 Ace 库。我使用 CDN:

<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js" type="text/javascript" charset="utf-8"></script>

要测试它,您可以将它们添加到另一个组件中:

const codeView = this.shadowRoot.querySelector('.codeView');
const snippet1 = new CodeSnippet('/testFile1.txt', 'line 1\nline 2\nline 3');
codeView.appendChild(snippet1);

const snippet2 = new CodeSnippet('/testFile2.txt', 'line 1\nline 2\nline 3\n');
codeView.appendChild(snippet2);

const snippet3 = new CodeSnippet('/testFile3.txt', '');
codeView.appendChild(snippet3);