I'm trying to change the cursor to a pointer in my application when features are hovered over. But I'm getting "TypeError: Cannot read property 'position' of undefined" when I mouseover the map before features load. Here's a sample I modified: JS Bin - Collaborative JavaScript Debugging
If the mouse is moving over the map when the page is being loaded, the error is thrown. If not then it works fine. What's the best way to deal with this? Or is there a better way of changing the cursor to pointer when hovering over features?
Solved! Go to Solution.
Brandon,
Ok, here is what I got to work without exception then:
<!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="[Access features with click events - 4.3]">
<!--
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>Access features with click events - 4.3</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#info {
background-color: black;
opacity: 0.75;
color: orange;
font-size: 18pt;
padding: 8px;
visibility: hidden;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.3/esri/css/main.css">
<script src="https://js.arcgis.com/4.3/"></script>
<script>
require([
"esri/core/watchUtils",
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/renderers/UniqueValueRenderer",
"esri/symbols/SimpleLineSymbol",
"dojo/dom",
"dojo/domReady!"
], function(
watchUtils,
Map,
MapView,
FeatureLayer,
UniqueValueRenderer,
SimpleLineSymbol,
dom
) {
var layer = new FeatureLayer({
url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/1",
outFields: ["*"]
});
var map = new Map({
basemap: "dark-gray",
layers: [layer]
});
var view = new MapView({
container: "viewDiv",
map: map,
center: [-61.125537, 35.863534],
zoom: 4
});
view.ui.add("info", "top-right");
function changeCursor(response){
if (response.results.length > 0){
document.getElementById("viewDiv").style.cursor = "pointer";
} else {
document.getElementById("viewDiv").style.cursor = "default";
}
}
function getGraphics(response) {
// the topmost graphic from the click location
// and display select attribute values from the
// graphic to the user
var graphic = response.results[0].graphic;
var attributes = graphic.attributes;
var category = attributes.CAT;
var wind = attributes.WIND_KTS;
var name = attributes.NAME;
dom.byId("info").style.visibility = "visible";
dom.byId("name").innerHTML = name;
dom.byId("category").innerHTML = "Category " + category;
dom.byId("wind").innerHTML = wind + " kts";
// symbolize all line segments with the given
// storm name with the same symbol
var renderer = new UniqueValueRenderer({
field: "NAME",
defaultSymbol: layer.renderer.symbol || layer.renderer.defaultSymbol,
uniqueValueInfos: [{
value: name,
symbol: new SimpleLineSymbol({
color: "orange",
width: 5,
cap: "round"
})
}]
});
layer.renderer = renderer;
}
view.then(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);
});
});
});
});
layer.then(function() {
// update the default renderer's
// symbol when the layer loads
var renderer = layer.renderer.clone();
renderer.symbol.width = 4;
renderer.symbol.color = [128, 128, 128, 0.8];
renderer.symbol.cap = "round";
layer.renderer = renderer;
});
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="info">
<span id="name"></span><br>
<span id="category"></span><br>
<span id="wind"></span>
</div>
</body>
</html>
Brandon,
You wait to add the event listener until the layer is loaded then:
<!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="[Access features with click events - 4.3]">
<!--
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>Access features with click events - 4.3</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#info {
background-color: black;
opacity: 0.75;
color: orange;
font-size: 18pt;
padding: 8px;
visibility: hidden;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.3/esri/css/main.css">
<script src="https://js.arcgis.com/4.3/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/renderers/UniqueValueRenderer",
"esri/symbols/SimpleLineSymbol",
"dojo/dom",
"dojo/domReady!"
], function(
Map,
MapView,
FeatureLayer,
UniqueValueRenderer,
SimpleLineSymbol,
dom
) {
var layer = new FeatureLayer({
url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/1",
outFields: ["*"]
});
var map = new Map({
basemap: "dark-gray",
layers: [layer]
});
var view = new MapView({
container: "viewDiv",
map: map,
center: [-61.125537, 35.863534],
zoom: 4
});
view.ui.add("info", "top-right");
function changeCursor(response){
if (response.results.length > 0){
document.getElementById("viewDiv").style.cursor = "pointer";
} else {
document.getElementById("viewDiv").style.cursor = "default";
}
}
function getGraphics(response) {
// the topmost graphic from the click location
// and display select attribute values from the
// graphic to the user
var graphic = response.results[0].graphic;
var attributes = graphic.attributes;
var category = attributes.CAT;
var wind = attributes.WIND_KTS;
var name = attributes.NAME;
dom.byId("info").style.visibility = "visible";
dom.byId("name").innerHTML = name;
dom.byId("category").innerHTML = "Category " + category;
dom.byId("wind").innerHTML = wind + " kts";
// symbolize all line segments with the given
// storm name with the same symbol
var renderer = new UniqueValueRenderer({
field: "NAME",
defaultSymbol: layer.renderer.symbol || layer.renderer.defaultSymbol,
uniqueValueInfos: [{
value: name,
symbol: new SimpleLineSymbol({
color: "orange",
width: 5,
cap: "round"
})
}]
});
layer.renderer = renderer;
}
view.then(function() {
layer.then(function() {
// update the default renderer's
// symbol when the layer loads
var renderer = layer.renderer.clone();
renderer.symbol.width = 4;
renderer.symbol.color = [128, 128, 128, 0.8];
renderer.symbol.cap = "round";
layer.renderer = renderer;
// 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);
});
});
});
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="info">
<span id="name"></span><br>
<span id="category"></span><br>
<span id="wind"></span>
</div>
</body>
</html>
Thanks for your reply Robert. Unfortunately the error is still being thrown. I added in some console logging and recorded this gif. (Notice it's critical that the mouse is in motion over the map as its loading)
view.then(function() {
console.log('view ready');
layer.then(function() {
console.log('layer ready');
...
Here's my current "solution":
view.on('pointer-move', function(evt){
if (!view.updating && view.ready){ // if view is updating, an error is raised
try {
view.hitTest({x: evt.x, y: evt.y}).then(changeCursor);
} catch (err) {
console.log(err);
}
}
});
The if statement isn't really doing anything -- I was searching for the right properties to watch. The try and catch blocks keep the app responding to mouse events but it seems like there should be a better way.
Brandon,
Ok, here is what I got to work without exception then:
<!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="[Access features with click events - 4.3]">
<!--
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>Access features with click events - 4.3</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#info {
background-color: black;
opacity: 0.75;
color: orange;
font-size: 18pt;
padding: 8px;
visibility: hidden;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.3/esri/css/main.css">
<script src="https://js.arcgis.com/4.3/"></script>
<script>
require([
"esri/core/watchUtils",
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/renderers/UniqueValueRenderer",
"esri/symbols/SimpleLineSymbol",
"dojo/dom",
"dojo/domReady!"
], function(
watchUtils,
Map,
MapView,
FeatureLayer,
UniqueValueRenderer,
SimpleLineSymbol,
dom
) {
var layer = new FeatureLayer({
url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/1",
outFields: ["*"]
});
var map = new Map({
basemap: "dark-gray",
layers: [layer]
});
var view = new MapView({
container: "viewDiv",
map: map,
center: [-61.125537, 35.863534],
zoom: 4
});
view.ui.add("info", "top-right");
function changeCursor(response){
if (response.results.length > 0){
document.getElementById("viewDiv").style.cursor = "pointer";
} else {
document.getElementById("viewDiv").style.cursor = "default";
}
}
function getGraphics(response) {
// the topmost graphic from the click location
// and display select attribute values from the
// graphic to the user
var graphic = response.results[0].graphic;
var attributes = graphic.attributes;
var category = attributes.CAT;
var wind = attributes.WIND_KTS;
var name = attributes.NAME;
dom.byId("info").style.visibility = "visible";
dom.byId("name").innerHTML = name;
dom.byId("category").innerHTML = "Category " + category;
dom.byId("wind").innerHTML = wind + " kts";
// symbolize all line segments with the given
// storm name with the same symbol
var renderer = new UniqueValueRenderer({
field: "NAME",
defaultSymbol: layer.renderer.symbol || layer.renderer.defaultSymbol,
uniqueValueInfos: [{
value: name,
symbol: new SimpleLineSymbol({
color: "orange",
width: 5,
cap: "round"
})
}]
});
layer.renderer = renderer;
}
view.then(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);
});
});
});
});
layer.then(function() {
// update the default renderer's
// symbol when the layer loads
var renderer = layer.renderer.clone();
renderer.symbol.width = 4;
renderer.symbol.color = [128, 128, 128, 0.8];
renderer.symbol.cap = "round";
layer.renderer = renderer;
});
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="info">
<span id="name"></span><br>
<span id="category"></span><br>
<span id="wind"></span>
</div>
</body>
</html>
Thank you so much for your question! I had the EXACT same issue when trying to do a hittest. I ended up using your try catch solution, because your final solution didn't seem to work in my case. I am updating the renderer after it loads, so even though updating is false once, it is actually still updating later and the event breaks if you try to move the mouse at the wrong time. The try catch worked great. Didn't realize there was a Try Catch in javascript. I understand it isn't the best possible solution, but oh well. Thanks again.