SVG foreignObject无法捕获onClick事件(ReactJS)

时间:2019-03-11 14:21:13

标签: javascript reactjs events svg

我正在使用React Digraph创建一个界面,用户可以在其中创建和编辑状态机。该界面有些可用(至少在通过键盘快捷键创建或删除节点方面),但是我需要更新该界面本身,以便用户只能使用鼠标来执行此操作。为此,我想到了以下界面:

interface

就代码而言,这非常容易。关于API,我只需要重写方法renderNode,并使用foreignObject元素将HTML插入到API使用的SVG上:

public renderNode = (nodeRef: any, data: any, id: string, selected: boolean, hovered: boolean) => {
    const positioning = data.type === 'required' ? styles.required : styles.optional;
    return (
      <g className={`shape`}>
        {!selected ? null : (
          <foreignObject x="-77" y="-77" width="154" height="77" xmlnsXlink="http://www.w3.org/1999/xlink">
            <i className={`fas fa-edit ${styles.btn} ${positioning} ${styles.edit}`} />
            <i
              className={`fas fa-minus-square ${styles.btn} ${positioning} ${styles.delete}`}
              onClick={() => {
                console.log('onClick');
                this.deleteNode(id);
              }}
            />
          </foreignObject>
        )}
        <use
          className={`node ${hovered ? 'hovered' : ''} ${selected ? 'selected' : ''}`}
          x="-77"
          y="-77"
          width="154"
          height="154"
          xlinkHref={`#${data.type}`}
        >
          <svg viewBox="-27 0 154 154" id={data.type} width="154" height="154">
            <rect transform="translate(50) rotate(45)" width="109" height="109" />
          </svg>
        </use>
      </g>
    );
  };

现在,一切都应按计划进行,但是由于某些原因,onClick事件没有被捕获。没有console.log显示,也没有调用deleteNode函数。

我正在使用:

    "react": "^16.6.1",
    "react-digraph": "^6.0.0",
    Google Chrome Version 72.0.3626.119 (Official Build) (64-bit)

知道为什么会这样吗?

谢谢。

修改
在James Delaney的建议之后,我尝试向所有可用节点添加引用以及事件监听器。由于我可能会从后端获取计算机的信息,因此无法静态创建所有引用。因此,我尝试在获取节点后在componentDidMount上创建“引用列表”,并在找到的所有节点上调用addEventListener方法,将事件侦听器添加到此列表中,并在出现以下情况时连接引用节点被渲染。实际上,这是componentDidMount

public componentDidMount(): void {
    const { machine } = this.props;
    this.setState({ graph: machine ? stateMachineToGraph(machine) : initialGraph }, () => {
      this.state.graph.nodes.forEach(node => {
        const del = `${node.id}delete`,
          edit = `${node.id}edit`;
        this[del].addEventListener('click', this.deleteNode(node.id));
        this[edit].addEventListener('click', this.editNode(node.id));
      });
    });
  }

这就是前面提到的foreignObject

<foreignObject x="-77" y="-77" width="154" height="77" xmlnsXlink="http://www.w3.org/1999/xlink">
            <i
              ref={edit => (this[`${id}edit`] = edit)}
              className={`fas fa-edit ${styles.btn} ${positioning} ${styles.edit}`}
            />
              <i
                ref={del => (this[`${id}delete`] = del)}
                className={`fas fa-minus-square ${styles.btn} ${positioning} ${styles.delete}`}
              />
          </foreignObject>

不幸的是,这不起作用,并且我尝试使用显式列表或静态使用引用(出于测试目的)遍历此解决方案。没有进展。

编辑2
考虑到上面可能的解决方案,我重复了一些操作,并且仅在呈现节点时才添加事件监听器。这样,renderNode方法就变成了这样:

public renderNode = (nodeRef: any, data: any, id: string, selected: boolean, hovered: boolean) => {
    console.log(id);
    if (this[`${id}edit`] && this[`${id}delete`]) {
      this[`${id}edit`].addEventListener('click', this.editNode(id));
      this[`${id}delete`].addEventListener('click', this.deleteNode(id));
    }
    const positioning = data.type === 'required' ? styles.required : styles.optional;
...

现在,我不测试特定节点的事件侦听器是否已存在,我将进一步进行测试。对于此实现,我最终可以删除节点,但是它无法识别出'click'事件。我只是将鼠标悬停在节点上,并触发该事件(以及“ edit”事件),然后删除该节点。为什么会这样?

谢谢。

1 个答案:

答案 0 :(得分:1)

  • 尝试将ref放在svg元素上
  • 在componentDidMount上将点击事件侦听器添加到参考
  • 删除componentWillUnmount中的事件侦听器

例如:

componentDidMount() {
    this.ref.addEventListener('click', this.deleteNode(id));
}

componentWillUnmount(){
    this.ref.removeEventListener('click', this.deleteNode(id));
}

}

我不确定这是否是解决方案,但是您可以尝试。