How to attach a click handler for an anchor in a custom HTMLElement in java script?

时间:2019-04-17 02:33:06

标签: javascript html typescript htmlelements

I am writing a HTMLElement and trying to add anchor tags to the html dynamically. I am able to add anchor tags to innerHtml (createLink function in my code), but onClick event handlers are missing. How can I attach onClick event handlers in javascript. I am writing my code in typescript and transpiling to js using babble and webpack.

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');
}

}

When it renders on the UI, I see this anchor tag, but the onClick event handler is missing.

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

2 个答案:

答案 0 :(得分:0)

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

看看this

答案 1 :(得分:0)

outerHTML

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);

潜在解决方案

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

一种替代方法是将单击处理委托给在构造过程中添加到自定义元素的单个处理程序,并添加data属性以链接包含用于访问私有类数据的雇员索引的元素(无法通过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,使其可以在此处运行
  • 在构造函数中添加了测试代码,
  • 修改了renderHtml以将getEmployeeTemplate用作地图函数,
  • 将第二个映射参数(即数组索引)传递给createLink
  • 简化了createLink

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