<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Highlight SceneLayer | Sample | ArcGIS Maps SDK for JavaScript</title>
<!-- Load the ArcGIS Maps SDK for JavaScript from CDN -->
<script type="module" src="https://js.arcgis.com/5.0/"></script>
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
--calcite-list-background-color-hover: rgb(255, 195, 10, 0.4);
<arcgis-scene item-id="fbbc829fa7d342e7ae8d18c54a5eab37">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle>
<arcgis-compass slot="top-left"> </arcgis-compass>
heading="Campus buildings"
selection-mode="multiple"
const [reactiveUtils, Query, Popup] = await $arcgis.import([
"@arcgis/core/core/reactiveUtils.js",
"@arcgis/core/rest/support/Query.js",
"@arcgis/core/widgets/Popup.js",
// Get DOM elements to display the list of buildings in the view
const buildingsPanel = document.getElementById("buildingsPanel");
const buildingsList = document.getElementById("buildingsList");
const viewElement = document.querySelector("arcgis-scene");
// Customize the popup which appears when clicking on a building in the view
viewElement.popup = new Popup({
// Ignore the default sizes that trigger responsive docking
position: "bottom-center",
// Add a "multiselect" highlight option for buildings selected in the list
// Add a "default" highlight option for when a building is hovered over in the list
viewElement.highlights = [
{ name: "default", color: "gold", fillOpacity: 0.2, haloColor: "gold", haloOpacity: 0.8 },
// Only one building can have the hover highlight applied at a time, so only one handler is needed to remove it
let hoverHighlight = null;
// Wait for the view to be ready
await viewElement.viewOnReady();
// Get the Buildings layer from the WebScene
const campusSceneLayer = viewElement.map.allLayers.filter((layer) => {
return layer.title === "Buildings";
// Define the attributes to use when querying the layer
campusSceneLayer.outFields = ["name"];
// Since the view is ready, update the building list to include all buildings in the Scene
updateBuildingList(campusSceneLayer, true);
// Hide the buiding list while the view is changing and update it once the view is stationary
viewElement.addEventListener("arcgisViewChange", (event) => {
// If the view is stationary, update and show the building list
// If the view is changing, hide the list
const visible = viewElement.stationary;
updateBuildingList(campusSceneLayer, visible);
// Updates the buildings list according to the current view
function updateBuildingList(layer, visible) {
viewElement.whenLayerView(layer).then((layerView) => {
// On initial load, add a new list item for every building in the Scene
if (buildingsList.children.length === 0) {
populateBuildingsList(layerView);
// Query for the features in the current view extent
const query = new Query({ geometry: viewElement.visibleArea });
layerView.queryFeatures(query).then((result) => {
// Update the description to include the number of buildings currently in view
buildingsPanel.description = `Currently in view: ${result.features.length} buildings`;
// Update the buildings list to include any which are currently visible in the view
for (const listItem of buildingsList.children) {
for (const feature of result.features) {
// If the list item matches a building currently in the view,
// show the list item and move on to the next one
if (listItem.label === feature.attributes.name) {
listItem.removeAttribute("closed");
for (const listItem of buildingsList.children) {
listItem.setAttribute("closed", "");
// Adds a new list item for every building in the Scene
function populateBuildingsList(layerView) {
// Wait for the layer view to be ready, then query for all loaded features
.whenOnce(() => !layerView.updating)
layerView.queryFeatures({}).then((result) => {
// Update the building list description to include the number of buildings currently in view
buildingsPanel.description = `Currently in view: ${result.features.length} buildings`;
result.features.forEach((feature) => {
// Create a list item element and action button
const li = document.createElement("calcite-list-item");
li.setAttribute("label", feature.attributes.name);
const action = document.createElement("calcite-action");
action.setAttribute("slot", "actions-end");
action.setAttribute("icon", "zoom-to-object");
action.setAttribute("scale", "s");
buildingsList.appendChild(li);
const objectId = feature.attributes.OID;
// When the list item is hovered over, highlight the corresponding building in gold
li.addEventListener("mouseenter", () => {
// Since no highlight name is provided, the default will be used
hoverHighlight = layerView.highlight(feature);
// Remove the hover highlight when the list item is no longer hovered over
li.addEventListener("mouseleave", () => {
hoverHighlight?.remove();
// When the list item is selected, highlight the corresponding building in blue
// Each list item needs a highlight handler so the highlight can be removed from each building independently
let selectionHighlight = null;
li.addEventListener("calciteListItemSelect", (event) => {
if (event.target.selected) {
// A feature can also be highlighted using its object ID
selectionHighlight = layerView.highlight(objectId, { name: "multiselect" });
selectionHighlight?.remove();
// When the action button is clicked, zoom to the building in the view
action.addEventListener("click", (event) => {
zoomToFeature(layerView, objectId);
// Zooms to a feature in the view
function zoomToFeature(layerView, objectId) {
// Query for the 3D extent of the feature based on its object ID
layerView.queryExtent({ objectIds: [objectId] }).then((result) => {
// Use the expand method to prevent zooming in too close to the feature
viewElement.goTo(result.extent.expand(4), { speedFactor: 0.5 });