1

我正在编写一个 HTMLElement 并尝试将锚标记动态添加到 html 中。我能够将锚标记添加到 innerHtml (createLink我的代码中的函数),但缺少 onClick 事件处理程序。如何在 javascript 中附加 onClick 事件处理程序。我正在用打字稿编写我的代码,并使用 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>
4

2 回答 2

0

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

看看这个

于 2019-04-17T03:24:02.773 回答
0

外层HTML

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

该语句使用HTML 片段序列化算法return anchor.outerHTML序列化锚元素。算法的第 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>

注意上面的片段有

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

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

于 2019-04-17T03:58:37.670 回答