We've almost reached the state of a fully operational layer tree. The next step in the process is to add the capabilities of changing layer attributes. In this example, we will make sure that we can change any layer's visibility, opacity, and its name from the GUI. The example files are named ch03_attributes. As we are only adding further parts to the layer element, we mostly have to extend our createRegistry method.
In our concept, only active layers can be modified. With this consideration, only the active layer element's options are exposed. The other layers will have the same nice, uniform height that shows only the layer's name and the visibility checkbox. We can easily implement this behavior with CSS:
.layercontainer .layer.active {
border-color: orange;
min-height: 2em;
height: auto;
}
.layer div {
display: none;
}
.layer.active div {
display: block;
}Now that the styling of the elements is complete, we can create logic to manage the layer attributes. First, we add some listeners to the layer's name element so that we can change it by double-clicking on it:
this.createRegistry = function (layer, buffer) {
[…]
layerSpan.addEventListener('dblclick', function () {
this.contentEditable = true;
layerDiv.classList.remove('ol-unselectable');
this.focus();
});
layerSpan.addEventListener('blur', function () {
if (this.contentEditable) {
this.contentEditable = false;
layer.set('name', this.textContent);
layerDiv.classList.add('ol-unselectable');
layerDiv.title = this.textContent;
this.scrollTo(0, 0);
}
});If we double-click on the layer name, our code changes its content to be editable, makes it selectable (otherwise, the editable attribute just wouldn't work on some browsers), and gives focus to it. If the element loses focus (the blur event), we restore the element's original status and update everything with the layer's new name. In the last step, we scroll to the start of the text as Firefox won't do it automatically.
Making an element's content editable is an easy solution, but it is not stable (add some line breaks to the layer name and see for yourself). As a more stable solution, you can add a hidden text input to the element, make it visible when it's double-clicked, bind it to the layer name and layer object correctly, and make it disappear on losing focus.
Next, we create a checkbox that controls the visibility of the layer. We control the top of it with CSS, and then bind it to the layer. We also gather its initial value from the layer object (luckily, both of them operate with Booleans):
var visibleBox = document.createElement('input');
visibleBox.type = 'checkbox';
visibleBox.className = 'visible';
visibleBox.checked = layer.getVisible();
visibleBox.addEventListener('change', function () {
if (this.checked) {
layer.setVisible(true);
} else {
layer.setVisible(false);
}
});
layerDiv.appendChild(this.stopPropagationOnEvent(visibleBox, 'click'));
[…]With the layer object given to the method, we only have to toggle its visibility based on the status of the checkbox. However, we are faced with a new issue. If we click on the checkbox, we trigger the selection event, making our code select the checkbox and messing up our entire logic. To prevent this behavior, we create a stopPropagationOnEvent utility method, which has a quite meaningful name:
layerTree.prototype.stopPropagationOnEvent = function (node, event) {
node.addEventListener(event, function (evt) {
evt.stopPropagation();
});
return node;
};In the last step, we return to the createRegistry method and define a slider, which we use to control the layer's opacity. We create a div element to store future controls and make sure that we can't deselect a layer by clicking on it. Next, we append the slider to it. Its initial value depends on the layer's current opacity, and it has a direct binding to the layer. Similar to the checkbox, we have to stop the slider from propagating, too:
var layerControls = document.createElement('div');
this.addSelectEvent(layerControls, true);
var opacityHandler = document.createElement('input');
opacityHandler.type = 'range';
opacityHandler.min = 0;
opacityHandler.max = 1;
opacityHandler.step = 0.1;
opacityHandler.value = layer.getOpacity();
opacityHandler.addEventListener('input', function () {
layer.setOpacity(this.value);
});
opacityHandler.addEventListener('change', function () {
layer.setOpacity(this.value);
});
layerControls.appendChild(this.stopPropagationOnEvent(opacityHandler, 'click'));
layerDiv.appendChild(layerControls);
[…]
};If you save the code and load the example, you will see the attribute controls working. If you want to map other attributes to the GUI, you can easily do so by creating elements for them and appending them to layerControls:
