javascript – 在Shadow DOM中的React Component时单击事件未触发
作者:互联网
我有一个特殊情况,我需要使用Web组件封装React组件.设置似乎很直接.这是React代码:
// React Component
class Box extends React.Component {
handleClick() {
alert("Click Works");
}
render() {
return (
<div
style={{background:'red', margin: 10, width: 200, cursor: 'pointer'}}
onClick={e => this.handleClick(e)}>
{this.props.label} <br /> CLICK ME
</div>
);
}
};
// Render React directly
ReactDOM.render(
<Box label="React Direct" />,
document.getElementById('mountReact')
);
HTML:
<div id="mountReact"></div>
这样安装正常,点击事件有效.现在,当我在React Component周围创建一个Web Component包装器时,它正确呈现,但click事件不起作用.这是我的Web组件包装器:
// Web Component Wrapper
class BoxWebComponentWrapper extends HTMLElement {
createdCallback() {
this.el = this.createShadowRoot();
this.mountEl = document.createElement('div');
this.el.appendChild(this.mountEl);
document.onreadystatechange = () => {
if (document.readyState === "complete") {
ReactDOM.render(
<Box label="Web Comp" />,
this.mountEl
);
}
};
}
}
// Register Web Component
document.registerElement('box-webcomp', {
prototype: BoxWebComponentWrapper.prototype
});
这是HTML:
<box-webcomp></box-webcomp>
有什么我想念的吗?或者React拒绝在Web组件中工作?我见过像Maple.JS这样的库可以做这种事,但是他们的库可以工作.我觉得我错过了一件小事.
这是CodePen,因此您可以看到问题:
http://codepen.io/homeslicesolutions/pen/jrrpLP
解决方法:
事实证明,Shadow DOM重新定位点击事件并将事件封装在阴影中. React不喜欢这样,因为它们本身不支持Shadow DOM,因此事件委托已关闭且事件未被触发.
我决定将事件重新绑定到实际的影子容器,这在技术上是“在光线下”.我使用event.path跟踪事件冒泡并在上下文中将所有React事件处理程序激活到阴影容器.
我添加了一个’retargetEvents’方法,它将所有可能的事件类型绑定到容器中.然后,它将通过查找“__reactInternalInstances”调度正确的React事件,并在事件范围/路径中查找相应的事件处理程序.
retargetEvents() {
let events = ["onClick", "onContextMenu", "onDoubleClick", "onDrag", "onDragEnd",
"onDragEnter", "onDragExit", "onDragLeave", "onDragOver", "onDragStart", "onDrop",
"onMouseDown", "onMouseEnter", "onMouseLeave","onMouseMove", "onMouseOut",
"onMouseOver", "onMouseUp"];
function dispatchEvent(event, eventType, itemProps) {
if (itemProps[eventType]) {
itemProps[eventType](event);
} else if (itemProps.children && itemProps.children.forEach) {
itemProps.children.forEach(child => {
child.props && dispatchEvent(event, eventType, child.props);
})
}
}
// Compatible with v0.14 & 15
function findReactInternal(item) {
let instance;
for (let key in item) {
if (item.hasOwnProperty(key) && ~key.indexOf('_reactInternal')) {
instance = item[key];
break;
}
}
return instance;
}
events.forEach(eventType => {
let transformedEventType = eventType.replace(/^on/, '').toLowerCase();
this.el.addEventListener(transformedEventType, event => {
for (let i in event.path) {
let item = event.path[i];
let internalComponent = findReactInternal(item);
if (internalComponent
&& internalComponent._currentElement
&& internalComponent._currentElement.props
) {
dispatchEvent(event, eventType, internalComponent._currentElement.props);
}
if (item == this.el) break;
}
});
});
}
当我将React组件渲染到shadow DOM中时,我会执行“retargetEvents”
createdCallback() {
this.el = this.createShadowRoot();
this.mountEl = document.createElement('div');
this.el.appendChild(this.mountEl);
document.onreadystatechange = () => {
if (document.readyState === "complete") {
ReactDOM.render(
<Box label="Web Comp" />,
this.mountEl
);
this.retargetEvents();
}
};
}
我希望这适用于React的未来版本.这是它工作的codePen:
http://codepen.io/homeslicesolutions/pen/ZOpbWb
感谢@mrlew的链接,它给了我如何解决这个问题的线索,也感谢@Wildhoney考虑与我相同的波长=).
标签:javascript,reactjs,web-component,shadow-dom,custom-element 来源: https://codeday.me/bug/20190930/1835562.html