当前位置: 首页 > 面试题库 >

Shadow DOM中的React Component时未触发Click事件

施俊哲
2023-03-14
问题内容

我有一个特殊的情况,我需要用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>

这可以很好地安装并且click事件起作用。现在,当我围绕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组件渲染到阴影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的未来版本。这是它的代码笔:

http://codepen.io/homeslicesolutions/pen/ZOpbWb

感谢@mrlew提供的链接为我提供了解决方法的线索,也感谢@Wildhoney考虑与我相同的波长=)。



 类似资料:
  • 使用jQuery-1.9.1。js和jQueryMobile 1.3.1(Chrome 26/Windows 7)我不明白为什么这些“点击”事件绑定到#一个触发,而另一个没有: 超文本标记语言: JavaScript: 当我在JSFiddle中运行它时,两个事件都会在没有包装在“mobile einit”事件中时触发:http://jsfiddle.net/9NRwa/ 我错过了什么?

  • 问题内容: 这可能是一个奇怪的情况,以前没有人曾经历过,但是我是偶然地发布此消息,因为有人知道我不知道的东西。 我正在使用jQuery 2.0.3和AngularJS。 如果我有这样的锚点: 然后它起作用了,当我单击它时,它输出“ click”。但是,当我将其放入包含该属性的模板中时,jQuery突然无法启动。如果我将脚本和锚点放在模板内部,则确实会触发。 使用使用模板的指令也是如此。这很奇怪,并

  • 问题内容: 我已经与React互动了几个月了,但是遇到了一些奇怪的事情。 在移动设备上根本不会触发点击事件: http://jsbin.com/morarewelu/1/edit?html,js,输出 http://jsbin.com/morarewelu/1/-在您的设备上检查 您可以看到该点击从未触发。在iPhone上进行测试。 这是预期的行为,还是仅仅是一个错误? 问题答案: React的G

  • 问题内容: 该主题很好地描述了我的问题,我假设它不会以这种方式起作用,是否有办法使其起作用?(解决方法)? 这是通过AJAX加载的代码: 这是我的点击事件: 问题答案: 做这个。 要么 编辑: 从jQuery 1.7开始,不推荐使用.live()方法。 使用.on()附加事件处理程序。较旧版本的jQuery的用户应使用.delegate()。

  • 问题内容: 我的整个项目都使用(Bluebird)Promises,但是有一个使用EventEmitter的特定库。 我想要实现以下目标: 我在Promises链中读了EventEmitter的答案。这给了我一种执行’connect’事件的回调的方法。这是我到目前为止所到之处 现在如何进一步链接“ eventB”? 问题答案: 我假设您想为每个事件做不同的事情。即使由的动作触发,您也可以将其视为另

  • 问题内容: 我有3个文件: js_json.js->用于我的json代码 javascript.js->用于我的javascript函数 index.php 这里的代码为: 这是我的代码: 这里的代码: 我的问题是: 当我单击链接“ Hola Test 1”时,它将起作用并显示消息。问题是,在单击选择选项之后,出现了链接“ Hola Test”,然后单击该链接(“ Hola Test”),该消息没