【问题标题】:How to get onClick Event for a Label in ChartJS and React?如何在 ChartJS 和 React 中获取标签的 onClick 事件?
【发布时间】:2021-04-25 15:01:07
【问题描述】:

我有一个带有标签的雷达图,我想在雷达图的标签上有一个点击事件,但元素总是返回 null。我已经特别关注了其他 Stack over flow 问题 1 和这个2。一个谈论用香草JS方法做这件事,另一个对我不起作用,有人能指出我做错了什么吗?

最终目标 -> 我想获取被点击的标签,并在该标签上添加一个删除线切换,以便我可以在雷达图中打开和关闭数据点。

我的实现

class Chart extends Component {
  constructor(props) {
    super(props);
    this.state = {
      chartData: props.chartData
    };
  }
  render() {
    return (
      <div className="chart">
        <Radar
          data={this.state.chartData}
          options={{
            title: {
              display: true,
              text: "Testing",
              fontSize: 25
            },
            legend: {
              display: true,
              position: "right"
            },
            onClick: function(evt, element) {
              // onClickNot working element null
              console.log(evt, element);
              if (element.length > 0) {
                console.log(element, element[0]._datasetInde);
                // you can also get dataset of your selected element
                console.log(data.datasets[element[0]._datasetIndex]);
              }
            }
          }}
        />
      </div>
    );
  }
}

**Link to the sample implementation **

【问题讨论】:

    标签: javascript reactjs chart.js


    【解决方案1】:

    注意:此答案实施不实施删除线。删除线可以通过在标签字符串的每个字符之间放置 unicode 字符 \u0366 来实现。这是example 如何使用 Javascript 执行此操作。我没有在这里展示这个的原因是因为当我在codesandbox上测试它时它看起来并不那么好。

    In a newer version of chart.js radial scale point label positions were exposed。在下面的示例中,我使用的是chart.js 版本3.2.0react-chartjs-2 版本3.0.3

    我们可以使用每个标签的边界框和点击位置来判断我们是否点击了一个标签。

    我在图表上使用了ref 来访问标签数据。

    我选择将标签对应的数据值设置为0。我这样做是因为如果您要删除与标签对应的元素,标签会随之消失。如果您在下面的演示中看到它的实际效果,我的选择可能更有意义。

    const swapPreviousCurrent = (data) => {
      const temp = data.currentValue;
      data.currentValue = data.previousValue;
      data.previousValue = temp;
    };
    
    class Chart extends Component {
      constructor(props) {
        super(props);
        this.state = {
          chartData: props.chartData
        };
        this.radarRef = {};
        this.labelsStrikeThrough = props.chartData.datasets.map((dataset) => {
          return dataset.data.map((d, dataIndex) => {
            return {
              data: {
                previousValue: 0,
                currentValue: d
              },
              label: {
                previousValue: `${props.chartData.labels[dataIndex]} (x)`,
                currentValue: props.chartData.labels[dataIndex]
              }
            };
          });
        });
      }
    
      render() {
        return (
          <div className="chart">
            <Radar
              ref={(radarRef) => (this.radarRef = radarRef)}
              data={this.state.chartData}
              options={{
                title: {
                  display: true,
                  text: "Testing",
                  fontSize: 25
                },
                legend: {
                  display: true,
                  position: "right"
                }
              }}
              getElementAtEvent={(element, event) => {
                const clickX = event.nativeEvent.offsetX;
                const clickY = event.nativeEvent.offsetY;
                const scale = this.radarRef.scales.r;
                const pointLabelItems = scale._pointLabelItems;
                pointLabelItems.forEach((pointLabelItem, index) => {
                  if (
                    clickX >= pointLabelItem.left &&
                    clickX <= pointLabelItem.right &&
                    clickY >= pointLabelItem.top &&
                    clickY <= pointLabelItem.bottom
                  ) {
                    // We've clicked inside the bounding box, swap labels and label data for each dataset
                    this.radarRef.data.datasets.forEach((dataset, datasetIndex) => {
                      swapPreviousCurrent(
                        this.labelsStrikeThrough[datasetIndex][index].data
                      );
                      swapPreviousCurrent(
                        this.labelsStrikeThrough[datasetIndex][index].label
                      );
    
                      this.radarRef.data.datasets[datasetIndex].data[
                        index
                      ] = this.labelsStrikeThrough[datasetIndex][
                        index
                      ].data.previousValue;
    
                      this.radarRef.data.labels[index] = this.labelsStrikeThrough[
                        datasetIndex
                      ][index].label.previousValue;
                    });
                    // labels and data have been changed, update the graph
                    this.radarRef.update();
                  }
                });
              }}
            />
          </div>
        );
      }
    }
    

    所以我使用图表上的ref 来访问标签位置,并使用getElementAtEventevent 使用event.nativeEvent.offsetX 和@987654339 来获得点击的xy 位置@。

    当我们点击标签时,我选择更新ref 的值并在0 与其实际值之间交换标签数据值。我将标签本身在自身和与'(x)' 连接的自身之间交换。

    sandbox example

    我这里没有使用 state 的原因是我不想在标签数据更新时重新渲染图表。

    【讨论】:

    • 这对我来说也是非常棒的学习和探索的东西,防止重新渲染的调整也非常好。我看到的一件事是边界记录非常小,它需要我们单击一个非常特定的区域,非常靠近标签开始的边缘,以便一键注册,否则需要多次单击 (2) 才能使其工作..让我看看我是否可以增加边界框记录标签
    • @INFOSYS 考虑小边界框的最简单方法是使if 语句更宽松。例如,您可以使用clickX &gt;= pointLabelItem.left - 30 而不是clickX &gt;= pointLabelItem.left。后者将有效地为您在左侧提供一些点击填充。你可以为其他方向做类似的事情。您可以从顶部和左侧方向减去一个值,然后在右侧和底部方向添加一个值。
    【解决方案2】:

    您可以运行一个修改数据集的函数:

    您将在拥有数据集的地方创建函数

    chartClick(index) {
        console.log(index);
        //this.setState({}) Modify your datasets properties
      }
    

    将函数作为道具传递

    <Chart chartClick={this.chartClick} chartData={this.state.chartData} />
    

    点击时执行函数

    onClick: (e, element) => {
                  if (element.length) {
                    this.props.chartClick(element[0]._datasetIndex);
                  }
                }
    

    【讨论】:

    • 这在点击点时有效,但在点击标签时无效。这也没有提到删除线。不过,这可以使用this 来完成。另外我认为对于 OP 的用例,它应该是 _index 而不是 _datasetIndex
    • @BasvanderLinden 你试过使用onElementsClick = {elems =&gt; {}} 吗?
    • 我试过getElementAtEventonElementsClick,如果你点击一个标签,它们都会返回一个空数组。适用于点,而不是标签。
    • @BasvanderLinden 即使以前使用 getElementsAtEvent 我也能得到它,我想要一些标签而不是这个点,这可能吗?
    • @danielm2402 是的,确实我需要它作为标签而不是数据点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-28
    • 2021-04-07
    • 2014-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-27
    相关资源
    最近更新 更多