试图将事件从某个组件传播到自定义HTML元素引发



我正在尝试制作一个控件,允许用户通过数字输入和滑块设置值。在阅读了相关文档后,这就是我带来的:

class NumberAndRange extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({mode: 'open'})
		
this.number = document.createElement('input')
this.number.type = 'number'
this.range = document.createElement('input')
this.range.type = 'range'
		
this.number.addEventListener('input',
()=>{this.range.value = this.number.value}
)
this.range.addEventListener('input',
()=>{this.number.value = this.range.value}
)
		
shadow.appendChild(this.number)
shadow.appendChild(this.range)
}
}
customElements.define('number-and-range', NumberAndRange)
<number-and-range></number-and-range>

正如您所看到的,它是有效的(至少在支持它的浏览器中)。为了简洁起见,我跳过了确保在numeric-and-range上设置minmaxstep等属性也在input上设置它们的代码,并且从numeric-and-range读取这些属性会返回有意义的结果。

但是,有一个功能我无法添加。显然,我们希望在numericrange上触发input事件也会在numeric-and-range上触发该事件;因此我们可能会忘记CCD_ 11实际上由两个输入组成,而是将其视为单个控件。因此:

class NumberAndRange extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({mode: 'open'})
		
this.number = document.createElement('input')
this.number.type = 'number'
this.range = document.createElement('input')
this.range.type = 'range'
		
this.number.addEventListener('input',
()=>{this.range.value = this.number.value}
)
this.range.addEventListener('input',
()=>{this.number.value = this.range.value}
)

const whole = this
this.number.addEventListener('input',
(ev)=>{whole.dispatchEvent(ev)}
)
this.range.addEventListener('input',
(ev)=>{whole.dispatchEvent(ev)}
)
		
shadow.appendChild(this.number)
shadow.appendChild(this.range)
}
}
customElements.define('number-and-range', NumberAndRange)
<number-and-range></number-and-range>

但这行不通!由于我无法理解的原因,移动滑块会抛出。InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable哪个对象不可用?为什么它不可用?我盯着我的代码看,真的找不到任何错误。

你能告诉我我做错了什么吗?

编辑:评论中提出的想法似乎对我也不起作用:

class NumberAndRange extends HTMLElement {
connectedCallback() {
const whole = this
this.number.addEventListener('input',
(ev)=>{whole.dispatchEvent(ev)}
)
this.range.addEventListener('input',
(ev)=>{whole.dispatchEvent(ev)}
)
}
constructor() {
super()
const shadow = this.attachShadow({mode: 'open'})
		
this.number = document.createElement('input')
this.number.type = 'numeric'
this.range = document.createElement('input')
this.range.type = 'range'
		
this.number.addEventListener('input',
()=>{this.range.value = this.number.value}
)
this.range.addEventListener('input',
()=>{this.number.value = this.range.value}
)
		
shadow.appendChild(this.number)
shadow.appendChild(this.range)
}
}
customElements.define('number-and-range', NumberAndRange)
<number-and-range></number-and-range>

感谢@Pavlo指出了问题:事件无法重新修补。因此,解决方案是创建一个新事件:

class NumberAndRange extends HTMLElement {
// Handling of the value attribute should probably be done better
// quick and dirty here
get value() {return this.number.value}
constructor() {
super()
const shadow = this.attachShadow({mode: 'open'})
		
this.number = document.createElement('input')
this.number.type = 'number'
this.range = document.createElement('input')
this.range.type = 'range'
		
this.number.addEventListener('input',
()=>{this.range.value = this.number.value}
)
this.range.addEventListener('input',
()=>{this.number.value = this.range.value}
)

const whole = this
this.number.addEventListener('input',
()=>{whole.dispatchEvent(new Event('input'))}
)
this.range.addEventListener('input',
()=>{whole.dispatchEvent(new Event('input'))}
)
		
shadow.appendChild(this.number)
shadow.appendChild(this.range)
}
}
customElements.define('number-and-range', NumberAndRange)
<number-and-range oninput="console.log(this.value)"></number-and-range>

您已经发现无法重新调度事件的问题。

但是您不需要调度任何自定义事件,使用默认的oninput事件

http://jsfiddle.net/dannye/6zk4vgc0

class NumberAndRange extends HTMLElement {
connectedCallback() {
let input = ['number','range'].map(
(type, idx) => 
Object.assign(
this.appendChild(document.createElement('input')),
{ 
type, 
min : 20,
max : 50,
value : 42,
oninput : _ => input[~~!idx].value = input[idx].value
}))
}
}
customElements.define('number-and-range', NumberAndRange);

注意:~~!idxidx == 0 ? 1 : 0相同,只是短了一些字节