JavaScript events
In addition, the terrariumElement that is passed to this function is assigned a pointerdown event, which is part of the web APIs designed to help with DOM management. onpointerdown fires when a button is pushed, or in our case, a draggable element is touched. This event handler works on both web and mobile browsers, with a few exceptions.
The event handler
onclickhas much more support cross-browser; why wouldn't you use it here? Think about the exact type of screen interaction you're trying to create here.
The pointerdrag Function
The terrariumElement is ready to be dragged around; when the onpointerdown event is fired, the function pointerDrag is invoked. Add that function right under this line: terrariumElement.onpointerdown = pointerDrag;:
function pointerDrag(e) {
e.preventDefault();
console.log(e);
pos3 = e.clientX;
pos4 = e.clientY;
}
Several things happen. First, you prevent the default events that normally happen on pointerdown from occurring by using e.preventDefault();. This way you have more control over the interface's behavior.
Second, right click on one plant in your browser and select Inspect Element then click on console. When you click a plant, you can see how the 'e' event is captured. Dig into the event to see how much information is gathered by one pointer down event!
Next, note how the local variables pos3 and pos4 are set to e.clientX. You can find the e values in the inspection pane. These values capture the x and y coordinates of the plant at the moment you click on it or touch it. You will need fine-grained control over the behavior of the plants as you click and drag them, so you keep track of their coordinates.
Is it becoming more clear why this entire app is built with one big closure? If it wasn't, how would you maintain scope for each of the 14 draggable plants?
Complete the initial function by adding two more pointer event manipulations under pos4 = e.clientY:
document.onpointermove = elementDrag;
document.onpointerup = stopElementDrag;
Now you are indicating that you want the plant to be dragged along with the pointer as you move it, and for the dragging gesture to stop when you deselect the plant. onpointermove and onpointerup are all parts of the same API as onpointerdown. The interface will throw errors now as you have not yet defined the elementDrag and the stopElementDrag functions, so build those out next.
The elementDrag and stopElementDrag Functions
You will complete your closure by adding two more internal functions that will handle what happens when you drag a plant and stop dragging it. The behavior you want is that you can drag any plant at any time and place it anywhere on the screen. This interface is quite un-opinionated (there is no drop zone for example) to allow you to design your terrarium exactly as you like it by adding, removing, and repositioning plants.
Add the elementDrag function right after the closing curly bracket of pointerDrag:
function elementDrag(e) {
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
console.log(pos1, pos2, pos3, pos4);
terrariumElement.style.top = terrariumElement.offsetTop - pos2 + 'px';
terrariumElement.style.left = terrariumElement.offsetLeft - pos1 + 'px';
}
In this function, you do a lot of editing of the initial positions 1-4 that you set as local variables in the outer function. What's going on here?
As you drag, you reassign pos1 by making it equal to pos3 (which you set earlier as e.clientX) minus the current e.clientX value. You do a similar operation to pos2. Then, you reset pos3 and pos4 to the new X and Y coordinates of the element. You can watch these changes in the console as you drag. Then, you manipulate the plant's css style to set its new position based on the new positions of pos1 and pos2, calculating the plant's top and left X and Y coordinates based on comparing its offset with these new positions.
offsetTopandoffsetLeftare CSS properties that set an element's position based on that of its parent; its parent can be any element that is not positioned as static.
All this recalculation of positioning allows you to fine-tune the behavior of the terrarium and its plants.
Final Task
The final task to complete the interface is to add the stopElementDrag function after the closing curly bracket of elementDrag:
function stopElementDrag() {
document.onpointerup = null;
document.onpointermove = null;
}
This small function resets the onpointerup and onpointermove events so that you can either restart your plant's progress by starting to drag it again, or start dragging a new plant.
Now you have compoleted your terrarium!
Solution: https://codepen.io/rommelnunez/pen/OJmxbyw