【问题标题】:How to attach a click handler for an anchor in a custom HTMLElement in java script?如何在 java 脚本的自定义 HTMLElement 中为锚点附加点击处理程序?
【发布时间】:2019-04-17 02:33:06
【问题描述】:

我正在编写一个 HTMLElement 并尝试将锚标记动态添加到 html 中。我可以将锚标记添加到 innerHtml(我的代码中的 createLink 函数),但缺少 onClick 事件处理程序。如何在 javascript 中附加 onClick 事件处理程序。我正在用 typescript 编写代码并使用 babble 和 webpack 转换为 js。

export class MyCustomElement extends HTMLElement {
constructor() {
    super();
}

private _serviceResponse: Employee[];

connectedCallback() {
    this.getData();
    this.innerHTML = '';
}

getData() {
    let fetch = window.fetch.bind(window);
    fetch('http://localhost/api/v1/values')
        .then(response => {
            return response.json();
        })
        .then((data: Employee[]) => {
            this._serviceResponse = data;
            console.log(this._serviceResponse);
            this.renderHtml();
        });
}

renderHtml() {
    this.innerHTML = `
            <table style="width: 100%;" border="2" >
                <thead>
                    <th>Header1</th>
                    <th>Header2</th>
                    <th>Header3</th>
                </thead>
                <tbody>
                    ${this._serviceResponse.map(employee => { return this.getEmployeeTemplate(employee); })}
                </tbody>
            </table>
            `;
}

getEmployeeTemplate(employee: Employee) {
    switch (employee.Type) {
        case "1":
            return this.getRegularTemplate(order);
        case "2":
            return `<tr><td colspan=3>Test Row}</td></tr>`;
    }
}

getRegularTemplate(emp: Employee): string {
    return `
    <tr>
        <td> ${emp.FirstName} </td>
        <td> ${emp.LastName} </td>
        <td>
            ${this.createLink(emp)}
        </td>
    </tr>
    `;
}

createLink(emp: Employee): string {
    var anchor = document.createElement('a');
    anchor.innerHTML = 'Details';
    anchor.onclick = () =>  { this.handleDetailsClick(emp); };
    anchor.href = '#';
    return anchor.outerHTML;
}

handleDetailsClick(emp: Employee) {
    console.log('Details link clicked: ' + emp);
}

handleDetailsClick() {
    console.log('clicked');
}

}

当它在 UI 上呈现时,我看到了这个锚标记,但缺少 onClick 事件处理程序。

<a href="#">Details</a>

【问题讨论】:

    标签: javascript html typescript htmlelements


    【解决方案1】:

    这是因为通过on&lt;event&gt; 属性添加事件处理程序不会影响 HTML 属性。

    看看this

    【讨论】:

      【解决方案2】:

      外部HTML

      createLink(emp: Employee): string {
          var anchor = document.createElement('a');
          anchor.innerHTML = 'Details';
          anchor.onclick = () =>  { this.handleDetailsClick(emp); };
          anchor.href = '#';
          return anchor.outerHTML;
      }
      

      return anchor.outerHTML 语句使用 HTML fragment serialization algorithm 序列化锚元素。算法的第 3.2 步描述了它如何获取元素的属性(具有文本值)并将它们转换为元素的标记。

      这一行:

       anchor.onclick = () =>  { this.handleDetailsClick(emp); };
      

      不创建属性 - 它创建附加到锚元素的函数属性(“方法”)。它不被序列化算法处理,并且不能作为文本字符串的一部分插入到自定义元素的 innerHTML 中。

      快速测试表明,即使是文本,点击处理程序也必须显式添加为要序列化的属性:

      "use strict";
      var anchor = document.createElement('a');
      anchor.innerHTML = 'Details';
      anchor.onclick = "clickHandle()"
      console.log("Setting a property doesn't work: " + anchor.outerHTML);
      anchor.setAttribute("onclick", "clickHandle");
      console.log("Setting an attribute does work: " + anchor.outerHTML);

      可能的解决方案

      设计目的是在单击链接时显示员工详细信息。限制是,如果必须在内部将链接作为文本处理,则它的点击处理程序必须从代码片段中调用全局点击处理程序 - 这会很臭。

      另一种方法是将点击处理委托给在构造期间添加到自定义元素的单个处理程序,并添加数据属性到包含用于访问私有类数据的员工索引的链接元素(无法从 HTML 访问源代码)。

      示例

      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="utf-8">
      <title>Custom Element</title>
      <script>
      
      class MyCustomElement extends HTMLElement {
      constructor() {
          super();
          this.onclick = event => {
              let link = event.target;
              let index = +link.dataset.index;
              if( link.tagName === 'A' && index >= 0) {
                  this.handleDetailsClick( this._serviceResponse[ index]);
              }
          };
          // FOR TESTING ONLY:
          function Employee( index) {
              this.FirstName = "Firstname" + index;
              this.LastName = "Lastname" + index;
              this.Type = "1";
          }
          this._serviceResponse = [ new Employee(1), new Employee(2)];
          setTimeout( this.renderHtml.bind(this), 10)
      }
       
      renderHtml() {
          this.innerHTML = `
                  <table style="width: 100%;" border="2" >
                      <thead>
                          <th>Header1</th>
                          <th>Header2</th>
                          <th>Header3</th>
                      </thead>
                      <tbody>
                          ${this._serviceResponse.map( this.getEmployeeTemplate.bind( this)).join(" ")}
                      </tbody>
                  </table>
                  `;
        }
      
      // ts: getEmployeeTemplate(employee: Employee, number: index ) {
      // js:
      
      getEmployeeTemplate( employee, index) {
          switch (employee.Type) {
              case "1":
                  return this.getRegularTemplate(employee, index);
              case "2":
                  return `<tr><td colspan=3>Test Row}</td></tr>`;
          }
      }
      
      // ts: getRegularTemplate(emp: Employee, number: index): string {
      // js:
      
      getRegularTemplate(emp, index){
          return `
          <tr>
              <td> ${emp.FirstName} </td>
              <td> ${emp.LastName} </td>
              <td>
                  ${this.createLink(index)}
              </td>
          </tr>
          `;
        }
      
      createLink(index){
          return `<a href="#" data-index="${index}">Details</a>`;
      }
      
      handleDetailsClick(emp) {
          console.log('Details link clicked: ', emp);
      }
      } // end of class
      
      customElements.define('employee-s', MyCustomElement);
      </script>
      
      </head>
      <body>
      
          <employee-s></employees>
      
      </body>
      </html>

      注意上面的sn-p有

      • 注释掉 TypeScript 以便它可以在这里运行,
      • 在构造函数中添加了测试代码,
      • 修改renderHtml以使用getEmployeeTemplate作为地图功能,
      • 将第二个映射参数(即数组索引)传递给createLink
      • 简化版createLink

      另请注意,从自定义标签创建自定义元素时不能有子节点 - 测试代码因此异步调用 renderHTML

      【讨论】:

      • 我尝试使用 setAttribute anchor.setAttribute('onClick', 'handleDetailsClick');。但是在浏览器上,找不到函数Uncaught ReferenceError: handleDetailsClick is not defined at HTMLAnchorElement.onclick
      • 这是因为属性是文本并且对于事件处理程序,被视为源代码 sn-p 并由 HTML 解析器独立于类定义进行编译 - 编译的事件处理程序无法访问类内部.
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-23
      • 1970-01-01
      • 1970-01-01
      • 2012-12-03
      • 1970-01-01
      相关资源
      最近更新 更多