Adding the UI event listeners
Summary
In this chapter, we will hook the UI event listeners up to our written functionality using the DisplayHelper
.
Concepts
Using browser event listeners to call HC code
Use the ‘oninput’ event listener
Here, we’ll use the oninput event listener to link the DisplayFilter
functionality to the slider UI elements and update the DOM to reflect user input.
If we take a look at the provided HTML template, we can determine the UI elements to be the four sliders (two sliders on each main category slider), the company buttons, the load model button, and the rendering options buttons. We have already implemented the load model button, so let’s focus on the other UI elements within the DisplayFilter
.
When the user moves one of the sliders, we want the DisplayFilter
class to update its filter values, gather the nodes that meet the newly set criterion, and render those nodes. We also want to protect for any UI quirks (such as setting the min slider higher than the max slider). For each slider, we will add an event listener, giving us four total event listeners for the sliders. However, each slider will enact the same series of calls, so we can abstract this out into its own main class member function, sliderOnInput()
.
sliderOnInput(slider) {
this._displayFilter.updateSliderRange(slider);
this._displayFilter.updateSliderLabels(slider);
this._displayFilter.gatherFilteredNodes(this._modelData);
this._displayFilter.setRenderingSelection();
}
The sliderOnInput
function takes a filter label, indicating which slider it is working with. The first thing we do is update the slider range that is tracked by the DisplayFilter
. We have not written this function yet, so let’s do so now.
updateSliderRange(id) {
let sliderElement = document.getElementById(`${id}Slider`);
let key = id + "Val";
this._sliderVals.set(key, sliderElement.value);
}
Notice that the slider naming is done consistently, so we can identify everything we need to do only given the “stock” or “price” identifier.
Next, we need to update the UI to display the new value of the slider and handle any odd behavior that may occur if passing one slider over another.
updateSliderLabels(filter) {
let id1 = filter.substring(0, 2) + "MinVal";
let id2 = filter.substring(0, 2) + "MaxVal";
let minVal = parseInt(this._sliderVals.get(id1));
let maxVal = parseInt(this._sliderVals.get(id2));
if (minVal >= maxVal) {
minVal = maxVal - 1;
this._sliders.get(id1).value = minVal.toString();
this._sliderVals.set(id1, minVal.toString());
return;
}
if (maxVal <= minVal) {
maxVal = minVal + 1;
this._sliders.get(id2).value = maxVal.toString();
this._sliderVals.set(id2, maxVal.toString());
return;
}
let valueLabels = document.querySelectorAll(`#${id1} , #${id2}`);
valueLabels[0].innerHTML = minVal.toString();
valueLabels[1].innerHTML = maxVal.toString();
}
First, we gather the stored values of the sliders from our object’s Map
. We then compare the values to ensure minimums and maximums have not passed over one another. If they have, we adjust the other slider accordingly. We then take these values and update the DOM elements displaying their values.
The next two functions should look familiar, as we wrote them earlier when creating the DisplayFilter
class. We will pass the application model data to the gatherFilteredNodes
call to gather the nodes that meet the criterion, and then call setRenderingSelection
to render our filtered nodes.
Finally, we have to pass the correct identifying filter label to the sliderOnInput
function. Set up an oninput
event listener for each slider handle, and pass the prefix of the handles ID to sliderOnInput
. (For example, id="psMinSlider"
would call sliderOnInput("psMin")
). Be sure to include these event listeners in the setEventListeners()
function, so they are called by the application constructor.
document.getElementById("psMinSlider").oninput = () => {
this.sliderOnInput("psMin");
};
document.getElementById("psMaxSlider").oninput = () => {
this.sliderOnInput("psMax");
};
document.getElementById("ssMinSlider").oninput = () => {
this.sliderOnInput("ssMin");
};
document.getElementById("ssMaxSlider").oninput = () => {
this.sliderOnInput("ssMax");
};
If we reload our application and load the moto model, moving the slider values will update the nodes meeting the rendering criterion, and the change will be displayed in the viewer.
Setup the event listeners
Finally, we will hook up the rendering selection buttons. When the user clicks on one of the radio button groups (whether it be the rendering mode or gradient mode), the DisplayFilter
will need to update its rendering mode and gradient selections. We can do this by making a public setter in the DisplayFilter
class for these two properties.
setFilterSelection(filter) {
this._filterSelection = filter;
}
setGradientSelection(choice) {
this._gradientSelection = choice;
}
And again, let’s set up the event listeners for these radio groups. Remember that we need to gather nodes and update the rendering on each of these actions too.
document.getElementsByName("displaymode").forEach((element) => {
let inputElement = element;
inputElement.onclick = () => {
this._displayFilter.setFilterSelection(inputElement.id);
this._displayFilter.gatherFilteredNodes(this._modelData);
this._displayFilter.setRenderingSelection();
};
});
document.getElementsByName("gradientmode").forEach((element) => {
let inputElement = element;
inputElement.onclick = () => {
this._displayFilter.setGradientSelection(inputElement.id);
this._displayFilter.setRenderingSelection();
};
});
Now as you click through each of these modes, you can see the rendering update in the viewer.
With this, all pieces of your tutorial should be complete. Feel free to build upon this example or experiment with additional APIs to further your learning.