如何从定义组件中处理和传递事件给点亮的组件



我正在尝试使用灯光元素设计web组件,我需要一个关于事件的帮助。正如我们从附加的代码片段中看到的,我们可以在html模板中使用@change="${this.handleEvent}",并在lit Element组件中处理函数handleEvent(e){}。通过这种方式,事件被限制和控制仅在lit web组件中。

然而,当其他人使用我们的web组件时,他们无法控制或访问定义组件中这些事件的值。例如,如果我们有一个index.html文件,我使用它像<my-element onchange="handleEvent(e)"></my-element>,我应该有访问onchange事件和调用索引文件内的函数。

那么,有没有一种方法来实现这一类似的行为常规html事件,而不是写事件,这是有限的在lit元素web组件

<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>
<script type="module">
import { LitElement, html, css } from 'https://unpkg.com/lit-element/lit-element.js?module';

class MyElement extends LitElement {

static get properties() {
return {
checked:  { type: Boolean, attribute: true }
};
}

static get styles() {
return [
css`
div {
padding: 10px;
width: 90px;
border: 2px solid orange;
}
`
];
}

render() {
return html`
<div>
<input
@change="${this.handleEvent}"
?checked="${this.checked}"
type="checkbox" /> Checkbox
</div>
`;
}

handleEvent(e) {
console.log(`Checkbox marked as: ${e.target.checked}`);
}
}
customElements.define('my-element', MyElement);
</script>
// Index.html
<my-element></my-element> 
// I am expecting to pass an event and handle it in the importing 
component. 
// Something like: **<my-element onchange="handleEvent(e)"}></my- 
element>**

如果您想要监听元素外部的事件,您应该像这样分派一个事件:

const event = new Event('my-event', {bubbles: true, composed: true});
myElement.dispatchEvent(event);

关于事件的Lit文档很好地概述了如何以及何时分派事件https://lit.dev/docs/components/events/#dispatching-events

通常对于您自己的自定义元素,您可能还需要定义您自己的API来提供给组件的消费者。这通常还需要定义你自己的组件发出的事件。

请看这个简单的(没有文字,但你明白了)例子:

customElements.define('foo-bar', class extends HTMLElement {
input = document.createElement('input');
constructor() {
super();
this.input.type = 'checkbox';
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(this.input);
this.input.addEventListener('change', this.handleChange.bind(this));
}
// replaces the internal change event with an event you publish to the outside world
// this event is part of your defined API.
handleChange(e) {
e.stopPropagation();
const stateChange = new CustomEvent('state-change', { 
bubbles: true, 
composed: true,
detail: { checked: this.input.checked }
});
this.dispatchEvent(stateChange);
}
});
document.addEventListener('state-change', (e) => { console.log(e.detail); })
<foo-bar></foo-bar>

如果您还希望像标准HTML元素那样支持声明性事件绑定(并且使用它被广泛认为是不好的做法),您可以使用观察到的属性来实现:

customElements.define('foo-bar', class extends HTMLElement {
input = document.createElement('input');
constructor() {
super();
this.input.type = 'checkbox';
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(this.input);
this.input.addEventListener('change', this.handleChange.bind(this));
this.declarativeValueChangeListener = this.declarativeValueChangeListener.bind(this);
}
// replaces the internal change event with an event you publish to the outside world
// this event is part of your defined API.
handleChange(e) {
e.stopPropagation();
const stateChange = new CustomEvent('state-change', { 
bubbles: true, 
composed: true,
detail: { value: this.input.value }
});
this.dispatchEvent(stateChange);
}

static get observedAttributes() { 
return [ 'onstatechange' ];
}

attributeChangedCallback(attr, oldVal, newVal) {
if (oldVal === newVal) return; // nothing changed, nothing to do here
if (newVal === null) { // attribute was removed
this.removeEventListener('state-change', this.declarativeValueChangeListener)
} else { // attribute was added
this.addEventListener('state-change', this.declarativeValueChangeListener)
}
}

declarativeValueChangeListener() {
const functionStr = this.getAttribute(this.constructor.observedAttributes[0]);
eval(functionStr);
}
});

function baz() { console.log('baz executed through declarative binding of an outside handler!'); }
<foo-bar onstatechange="baz()"></foo-bar>

我不明白为什么你需要事件,
单击input上可以执行全局函数或局部方法。

不需要老旧的bind繁文缛节,不需要observedAttributes

可以使用默认的onchange,因为所有事件都存在于HTMLElement
只有输入比如textarea实际上fireChange Event

在任何其他HTMLElement上使用都不会发生任何事情,您必须自己调用this.onchange()document.querySelector("foo-bar").onchange()
可以使用您自己的属性名,如将始终是一个字符串,而不会被浏览器引擎解析为JS代码。

你需要eval(code)来执行组件作用域中的代码,并使onchange="this.baz()"工作。

customElements.define('foo-bar', class extends HTMLElement {
constructor() {
let input = document.createElement("input");
input.type = "checkbox";
super()
.attachShadow({mode: 'open'}) 
.append(input,"click me to execute ", this.getAttribute("onchange"));
this.onclick = (evt) => { // maybe you only want input.onclick
//evt.stopPropagation();
let code = this.getAttribute("onchange");
try {
eval(code); // this.onchange() will only execute global functions
} catch (e) {
console.error(e,code);
}
}
}
baz() {
console.log("Executed baz Method");
}
});
function baz() {
console.log("Executed baz Function");
}
<foo-bar onchange="baz()"></foo-bar>
<foo-bar onchange="this.baz()"></foo-bar>
<style>
foo-bar { display:block; zoom:2 }
</style>

重要提示

shadowDOM是你的救命稻草。

onchange事件frominput转义shadowDOM
(像composed:true的自定义事件)

如果没有shadowDOM,父元素上的所有onchange声明都将触发,因为Event会显示:

<div onchange="console.log(666)">
<input onchange="console.log(this)" type="checkbox">
</div>
<style>
input { zoom:3 }
</style>

这是一个很好的例子,一个shadowRoot在一个常规的HTMLElement上可以有value,而不需要声明自定义元素

相关内容

  • 没有找到相关文章

最新更新