Feature Layer Hover to open Popup Template - ArcGIS JS 4.16

3807
4
Jump to solution
08-21-2020 10:27 AM
GeorgeAbraham
New Contributor III

Hi Robert Scheitlin, GISP / Ken Buja,

Similar to this ArcGIS API for JavaScript Sandbox  how can I achieve the same on the latest JS 4.16.

Basically I am manually adding a FeatureLayer and also set a popupTemplate to it, however, I want the Popup Template to open on mouse hover over the Feature Layer and not on click.

I tried layer.on("mouse-over", function (evt) {}) and also view.on("pointer-move", function (evt) {}), but the code inside is not triggering.

Libraries added - dojo/on, dojo/domReady!

0 Kudos
1 Solution

Accepted Solutions
KenBuja
MVP Esteemed Contributor

This is one way to do it, updating an example Robert Scheitlin, GISP‌ created for another question

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <meta name="description" content="[Highlight features with hover events - 4.11]">
  <!--
  ArcGIS API for JavaScript, https://js.arcgis.com
  For more information about the view-hittest sample, read the original sample description at developers.arcgis.com.
  https://developers.arcgis.com/javascript/latest/view-hittest/index.html
  -->
  <title>Highlight features with hover events - 4.11</title>

  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

  <link rel="stylesheet" href="https://js.arcgis.com/4.11/esri/css/main.css">
  <link rel="stylesheet" href="https://js.arcgis.com/4.11/dijit/themes/claro/claro.css">
  <script src="https://js.arcgis.com/4.11/"></script>

  <script>
    var dialog, dRenderer;
    require([
      "esri/core/watchUtils",
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/FeatureLayer",
      "esri/renderers/UniqueValueRenderer",
      "esri/symbols/SimpleLineSymbol",
      "dojo/dom",
      "dojo/dom-style",
      "dijit/popup",
      "dojo/domReady!"
    ], function (
      watchUtils,
      Map,
      MapView,
      FeatureLayer,
      UniqueValueRenderer,
      SimpleLineSymbol,
      dom,
      domStyle,
      dijitPopup
    ) {

      var layer = new FeatureLayer({
        url: "https://services.arcgis.com/8Pc9XBTAsYuxx9Ny/arcgis/rest/services/BuildingFootprint2D_gdb/FeatureServer/0",
        outFields: ["*"]
      });

      var map = new Map({
        basemap: "dark-gray",
        layers: [layer]
      });

      var view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-80.135073, 25.774479],
        zoom: 16
      });

      function changeCursor(response) {
        if (response.results.length > 0) {
          document.getElementById("viewDiv").style.cursor = "pointer";
        } else {
          document.getElementById("viewDiv").style.cursor = "default";
        }
      }

      function getGraphics(response) {
        view.graphics.removeAll();
        if (response.results.length > 0) {
          var graphic = response.results[0].graphic;
          graphic.symbol = {
            type: "simple-fill",
            style: "none",
            outline: { // autocasts as new SimpleLineSymbol()
              color: "orange",
              width: 1
            }
          }
          view.graphics.add(graphic);
        }
      }

      function showPopup(response) {
        view.popup.close();
        if (response.results.length > 0) {
          view.popup.open({
            title: 'Building ID: ' + response.results[0].graphic.attributes.UNIQUEID,
            content: 'Year Updated: ' + response.results[0].graphic.attributes.YEARUPDATE,
            location: response.results[0].graphic.geometry.centroid
          });
        }
      }

      view.when(function () {
        view.whenLayerView(layer).then(function (lview) {
          watchUtils.whenFalseOnce(lview, "updating", function () {
            // Set up a click event handler and retrieve the screen x, y coordinates
            view.on("pointer-move", function (evt) {
              var screenPoint = {
                x: evt.x,
                y: evt.y
              };

              // the hitTest() checks to see if any graphics in the view
              // intersect the given screen x, y coordinates
              view.hitTest(screenPoint)
                .then(function (response) {
                  changeCursor(response);
                  getGraphics(response);
                  showPopup(response);
                });
            });
          });
        });
      });
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>

View solution in original post

4 Replies
KenBuja
MVP Esteemed Contributor

This is one way to do it, updating an example Robert Scheitlin, GISP‌ created for another question

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
  <meta name="description" content="[Highlight features with hover events - 4.11]">
  <!--
  ArcGIS API for JavaScript, https://js.arcgis.com
  For more information about the view-hittest sample, read the original sample description at developers.arcgis.com.
  https://developers.arcgis.com/javascript/latest/view-hittest/index.html
  -->
  <title>Highlight features with hover events - 4.11</title>

  <style>
    html,
    body,
    #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
    }
  </style>

  <link rel="stylesheet" href="https://js.arcgis.com/4.11/esri/css/main.css">
  <link rel="stylesheet" href="https://js.arcgis.com/4.11/dijit/themes/claro/claro.css">
  <script src="https://js.arcgis.com/4.11/"></script>

  <script>
    var dialog, dRenderer;
    require([
      "esri/core/watchUtils",
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/FeatureLayer",
      "esri/renderers/UniqueValueRenderer",
      "esri/symbols/SimpleLineSymbol",
      "dojo/dom",
      "dojo/dom-style",
      "dijit/popup",
      "dojo/domReady!"
    ], function (
      watchUtils,
      Map,
      MapView,
      FeatureLayer,
      UniqueValueRenderer,
      SimpleLineSymbol,
      dom,
      domStyle,
      dijitPopup
    ) {

      var layer = new FeatureLayer({
        url: "https://services.arcgis.com/8Pc9XBTAsYuxx9Ny/arcgis/rest/services/BuildingFootprint2D_gdb/FeatureServer/0",
        outFields: ["*"]
      });

      var map = new Map({
        basemap: "dark-gray",
        layers: [layer]
      });

      var view = new MapView({
        container: "viewDiv",
        map: map,
        center: [-80.135073, 25.774479],
        zoom: 16
      });

      function changeCursor(response) {
        if (response.results.length > 0) {
          document.getElementById("viewDiv").style.cursor = "pointer";
        } else {
          document.getElementById("viewDiv").style.cursor = "default";
        }
      }

      function getGraphics(response) {
        view.graphics.removeAll();
        if (response.results.length > 0) {
          var graphic = response.results[0].graphic;
          graphic.symbol = {
            type: "simple-fill",
            style: "none",
            outline: { // autocasts as new SimpleLineSymbol()
              color: "orange",
              width: 1
            }
          }
          view.graphics.add(graphic);
        }
      }

      function showPopup(response) {
        view.popup.close();
        if (response.results.length > 0) {
          view.popup.open({
            title: 'Building ID: ' + response.results[0].graphic.attributes.UNIQUEID,
            content: 'Year Updated: ' + response.results[0].graphic.attributes.YEARUPDATE,
            location: response.results[0].graphic.geometry.centroid
          });
        }
      }

      view.when(function () {
        view.whenLayerView(layer).then(function (lview) {
          watchUtils.whenFalseOnce(lview, "updating", function () {
            // Set up a click event handler and retrieve the screen x, y coordinates
            view.on("pointer-move", function (evt) {
              var screenPoint = {
                x: evt.x,
                y: evt.y
              };

              // the hitTest() checks to see if any graphics in the view
              // intersect the given screen x, y coordinates
              view.hitTest(screenPoint)
                .then(function (response) {
                  changeCursor(response);
                  getGraphics(response);
                  showPopup(response);
                });
            });
          });
        });
      });
    });
  </script>
</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>
GeorgeAbraham
New Contributor III

Hi Ken Buja,

Thanks a lot for this solution - this helps a lot.

I had to make some modifications to suit my code since the pointer-move is at the view level, so filtering out in hitTest if its my layer of interest, also didn't need setCursor and getGraphics method etc.

I had one concern - the way the popup comes and goes, like even if I am on the same layer, every mouse movement its opening the new popup and closing the old even if I am on the same layer.

I tried various approaches like if the OBJECTID of the feature layer is same where I am hovering to, then don't call the popup, etc, but the flicker is there on opening the popup.

0 Kudos
RobertScheitlin__GISP
MVP Emeritus

George,

   Here is some code for smooth hover popups.

<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no" />
  <title>Smooth Hover Popup</title>

  <link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/themes/light/main.css" />
  <style>
    html,
     body,
     #viewDiv {
       padding: 0;
       margin: 0;
       height: 100%;
       width: 100%;
     }
  </style>
  <script src="https://js.arcgis.com/4.15/"></script>
  <script>
    var populationChange;
    require([
      "esri/Map",
      "esri/views/MapView",
      "esri/layers/Layer"
    ], function (Map, MapView, Layer) {
      var map = new Map({
        basemap: "dark-gray"
      });

      // Create the MapView
      var view = new MapView({
        container: "viewDiv",
        map: map,
        zoom: 7,
        center: [-87, 34]
      });

      Layer.fromPortalItem({
        portalItem: {
          // autocasts as new PortalItem()
          id: "e8f85b4982a24210b9c8aa20ba4e1bf7"
        }
      }).then(function (layer) {
        // add the layer to the map
        layer.outFields = ["*"];
        map.add(layer);

        view.when().then((_) => {
          view.whenLayerView(layer).then((layerView) => {
            let lResult = {attributes:{NAME:null}};
            view.on("pointer-move", (event) => {
              view.hitTest(event).then(({
                results
              }) => {
                const result = results[0];
                if (result) {
                  if(result.graphic.layer.id === layer.id){
                    if(lResult.attributes.NAME !== result.graphic.attributes.NAME){
                      view.popup.open({
                        features: [result.graphic],
                        location: result.graphic.geometry.extent.center
                      });
                      lResult = result.graphic;
                    }
                  }
                }else{
                  view.popup.close();
                }
              });
            });
          });
        });

        // Create a new popupTemplate for the layer and
        // format the numeric field values using the FieldInfoFormat properties. Call the custom populationChange()
        // function to calculate percent change for the county.
        var popupTemplate = {
          // autocasts as new PopupTemplate()
          title: "Population in {NAME}",
          outFields: ["*"],
          content: populationChange,
          fieldInfos: [{
              fieldName: "POP2010",
              format: {
                digitSeparator: true,
                places: 0
              }
            },
            {
              fieldName: "POP10_SQMI",
              format: {
                digitSeparator: true,
                places: 2
              }
            },
            {
              fieldName: "POP2013",
              format: {
                digitSeparator: true,
                places: 0
              }
            },
            {
              fieldName: "POP13_SQMI",
              format: {
                digitSeparator: true,
                places: 2
              }
            }
          ]
        };

        layer.popupTemplate = popupTemplate;

        function populationChange(feature) {
          var div = document.createElement("div");
          var upArrow =
            '<svg width="16" height="16" ><polygon points="14.14 7.07 7.07 0 0 7.07 4.07 7.07 4.07 16 10.07 16 10.07 7.07 14.14 7.07" style="fill:green"/></svg>';
          var downArrow =
            '<svg width="16" height="16"><polygon points="0 8.93 7.07 16 14.14 8.93 10.07 8.93 10.07 0 4.07 0 4.07 8.93 0 8.93" style="fill:red"/></svg>';

          // Calculate the population percent change from 2010 to 2013.
          var diff =
            feature.graphic.attributes.POP2013 - feature.graphic.attributes.POP2010;
          var pctChange = (diff * 100) / feature.graphic.attributes.POP2010;
          var arrow = diff > 0 ? upArrow : downArrow;

          // Add green arrow if the percent change is positive and a red arrow for negative percent change.
          div.innerHTML =
            "As of 2010, the total population in this area was <b>" +
            feature.graphic.attributes.POP2010 +
            "</b> and the density was <b>" +
            feature.graphic.attributes.POP10_SQMI +
            "</b> sq mi. As of 2013, the total population was <b>" +
            feature.graphic.attributes.POP2013 +
            "</b> and the density was <b>" +
            feature.graphic.attributes.POP13_SQMI +
            "</b> sq mi. <br/> <br/>" +
            "Percent change is " +
            arrow +
            "<span style='color: " +
            (pctChange < 0 ? "red" : "green") +
            ";'>" +
            pctChange.toFixed(3) +
            "%</span>";
          return div;
        }
      });
    });
  </script>


</head>

<body>
  <div id="viewDiv"></div>
</body>

</html>
GeorgeAbraham
New Contributor III

Thanks a lot Robert Scheitlin, GISP, this looks good, implementing it and checking, but the sample is working smooth. 

I think the issue was with how I was calling and the structuring.

0 Kudos