SVG foreignObject не перехватывает событие onClick (ReactJS) - PullRequest
0 голосов
/ 11 марта 2019

Я использую React Digraph , чтобы создать интерфейс, где пользователь может создавать и редактировать конечные автоматы. Интерфейс несколько удобен (по крайней мере, с точки зрения создания или удаления узлов с помощью сочетаний клавиш), но мне нужно обновить сам интерфейс, чтобы пользователь мог сделать это только с помощью мыши. Для этого я представил следующий интерфейс:

interface

С точки зрения кода это было довольно легко. Что касается API, мне просто пришлось переопределить метод renderNode и использовать элемент foreignObject для вставки HTML в SVG, используемый API:

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)

Есть идеи, почему это происходит?

Заранее спасибо.

Редактировать
После предложения Джеймса Делани я попытался добавить ссылки и, впоследствии, прослушиватели событий для всех доступных узлов. Поскольку я потенциально получаю информацию о машине из бэкэнда, я не могу статически создавать все ссылки. Поэтому я попытался создать «список ссылок» на 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 Ответ

1 голос
/ 11 марта 2019
  • Попробуйте поставить ref на элемент svg
  • на componentDidMount добавить прослушиватель события click для ref
  • удалить прослушиватель события в componentWillUnmount

Например:

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

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

}

Я не уверен, что это решение, но вы можете попробовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...