Plot feature attributes on a dynamic chart that updates as users pan and zoom, and respond to chart interactions by modifying feature layer contents. This demo relies on Chart.js to render an interactive scatterplot.
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Add a dynamic chart (Chart.js)</title>
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" crossorigin="">
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" crossorigin=""></script>
    <!-- Load Esri Leaflet from CDN -->
    <script src="https://unpkg.com/esri-leaflet@3.0.19/dist/esri-leaflet.js"></script>
    <!-- Load Esri Leaflet Vector from CDN -->
    <script src="https://unpkg.com/esri-leaflet-vector@4.3.2/dist/esri-leaflet-vector.js" crossorigin=""></script>
    <style>
      html,
      body,
      #map {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
        font-family: Arial, Helvetica, sans-serif;
        font-size: 14px;
        color: #323232;
      }
    </style>
  </head>
  <body>
    <!-- Load Chart.js from CDN -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js@3.7.0/dist/chart.min.js"></script>
    <style>
      .chart-container {
        height: 245px;
        width: 245px;
      }
      #info-pane {
        position: absolute;
        top: 10px;
        right: 10px;
        z-index: 400;
        padding: 1em;
        background: white;
      }
    </style>
    <div id="map"></div>
    <div id="info-pane" class="leaflet-bar chart-container">
      <canvas id="chartCanvas"></canvas>
    </div>
    <script>
      const accessToken = "YOUR_ACCESS_TOKEN";
      const map = L.map("map").setView([45.526, -122.667], 13);
      L.esri.Vector.vectorBasemapLayer("arcgis/community", {
        token: accessToken
      }).addTo(map);
      //Features from this layer will appear in the Chart.js scatterplot
      const treesFeatureLayer = L.esri.featureLayer({
        url: "https://www.portlandmaps.com/arcgis/rest/services/Public/Parks_Misc/MapServer/21/"
      });
      treesFeatureLayer.addTo(map);
      const initialChartData = { //Define a chart
        datasets: [
          {
            label: "Portland Heritage Trees",
            data: [] //Data will be update dynamically below
          }
        ]
      };
      const chartOptions = {
        scales: {
          x: {
            title: {
              display: true,
              text: "tree diameter"
            },
            beginAtZero: true,
            max: 250,
            ticks: {
              stepSize: 50
            }
          },
          y: {
            title: {
              display: true,
              text: "tree height"
            },
            beginAtZero: true,
            max: 250,
            ticks: {
              stepSize: 50
            }
          }
        },
        maintainAspectRatio: false, // Turn off animations during chart data updates
        animation: false,
        onHover: handleChartHover
      };
      const chart = new Chart("chartCanvas", {
        type: "scatter",
        data: initialChartData,
        options: chartOptions
      });
      map.on("zoom move", updateChart); //Show in the chart only the features in the map's current extent
      treesFeatureLayer.on("load", updateChart);
      function updateChart() {
        // reformat the features' attributes of interest into
        // the data array format required by the Chart.js scatterplot
        const scatterPlotDataArray = [];
        treesFeatureLayer.eachActiveFeature(function (e) {
          scatterPlotDataArray.push({
            x: e.feature.properties.DIAMETER,
            y: e.feature.properties.HEIGHT,
            featureId: e.feature.id
          });
        });
        // assign the new scatterPlotDataArray to the chart's data property
        chart.data.datasets[0].data = scatterPlotDataArray;
        chart.update();
      }
      function handleChartHover(event, elements, chart) {
        if (!elements.length) {
          treesFeatureLayer.eachFeature(function (e) {
            e.setOpacity(1);
            e.setZIndexOffset(0);
          });
          return;
        }
        const hoverFeatureIds = elements.map(function (datum) {
          return chart.data.datasets[datum.datasetIndex].data[datum.index].featureId;
        });
        treesFeatureLayer.eachFeature(function (e) {
          if (hoverFeatureIds.indexOf(e.feature.id) > -1) {
            e.setOpacity(1);
            e.setZIndexOffset(10000);
          } else {
            e.setOpacity(0.1);
          }
        });
      }
    </script>
  </body>
</html>