Course: JavaScript

Progress (0%)

Exercise: Pointer Circle

Exercise 56 Easy

Prerequisites for the exercise

  1. JavaScript Mouse Events
  2. All previous chapters

Objective

Create a script to make the mouse-click interaction more visible to the viewer of the screen.

Description

As you might already know, as you interact with a mouse, a person viewing your screen isn't ever able to tell when exactly you click the mouse, unless the click obviously triggers a reaction from the browser, or even better, there is a customized pointer set up.

In other words, during the click, the pointer stays the exact same — it doesn't change shape, it doesn't change color, nothing else gets displayed on the screen, just about nothing.

In this exercise, you have to make the mouse interaction clearly visible when watched by another viewer.

And here's how to make the interaction visible:

  • When the mouse button goes down, a light blue circle must be shown right at the point of contact of the mouse pointer.
  • Then with the mouse button still down, if the pointer is moved around, the circle must move alongside it.
  • Lastly, when the mouse button is released, the circle must disappear.

Note that at the press of the right button, the aforementioned behavior must be repeated, but with the circle's color orange instead of blue.

Here's how the final result should work.

Live Example

Simple?

Note that your solution MUST NOT use CSS directly. That is, you must not use HTML classes in order to get the corresponding CSS applied, and must only use JavaScript. This doesn't obviously mean that you can't use CSS — that would've meant no styling at all!

In simpler words, you can only do the styling from within JavaScript.

Hints

Hint 1

Use the style property of the HTMLElement interface in order to apply CSS styles from within the JavaScript.

Hint 2

Use the clientX and clientY properties of the mousedown event in order to get the coordinates of the pointer.

Hint 3

Use the button property of the mousedown event to determine which mouse button was pressed.

View Solution

New file

Inside the directory you created for this course on JavaScript, create a new folder called Exercise-56-Pointer-Circle and put the .html solution files for this exercise within it.

Solution

First, we'll create a new <div> element node to act as the pointer circle:

var circleElement = document.createElement('div');

Since there's no need to create multiple circles, there's likewise no need to encapsulate this entire logic inside a separate function. We'll write all of our code for the exercise's solution in the top-most level.

And we'll create the circle just once and then merely show/hide it on given mouse events by toggling CSS styles on it. If we want to, we could even remove the circle from the DOM and then add it again on given events, but that would only make the whole implementation more complex than it ideally should be. It's a simple task, so let's keep it simple.

Up next, we'll apply some styles to it:

var circleElement = document.createElement('div');

circleElement.style.cssText = `
   position: fixed;
   z-index: 1000;
   height: 20px;
   width: 20px;
   border-radius: 10px;
   transform: translate(-50%, -50%);
   background-color: paleturquoise;
   display: none;
`;

Notice how we apply the styles all at once using the cssText property of the element node's style object. This is to reduce the constant repetition of the starting expression circleElement.style. if we applied each and every style individually on style.

Also notice our use of template literals (``) in order to nicely format the style string.

Moving on, next we'll set up a couple of mouse event listeners on window in order to track mouse events happening anywhere on the webpage.

  1. On mousedown, we'll display the circle by applying the display: block style to it. Also, we'll check if the event was triggered due to the right button, with the help of its button property, in which case we'll change the background of the circle to an orange color.
  2. On mousemove, we'll simply update the left and top style properties based on the values of the event's clientX and clientY properties, respectively.
  3. On mouseup, we'll hide the circle again by applying display: none on it, and even reset its background color to the original light blue.

Based on these simple descriptions, here are the handlers for mousedown, mousemove and mouseup:

/* ... */

window.addEventListener('mousedown', function(e) {
   e.preventDefault();
   if (e.button === 2) {
      circleElement.style.backgroundColor = 'orange';
   }
   circleElement.style.display = 'block';
});

window.addEventListener('mousemove', function(e) {
   circleElement.style.left = `${e.clientX}px`;
   circleElement.style.top = `${e.clientY}px`;
});

window.addEventListener('mouseup', function(e) {
   circleElement.style.backgroundColor = 'paleturquoise';
   circleElement.style.display = 'none';
});

Notice the call to e.preventDefault() inside the mousedown handler. It's purely to make sure that the activation behavior associated with mousedown doesn't fire and, likewise, doesn't interfere with our program.

Typically, this behavior is an aggregate of mousedown followed by mousemove, and tends to make text selections or drag objects. With preventDefault() called in mousedown, it ceases to happen.

Keep in mind that there's absolutely no strict necessity of including a call to preventDefault() in your mousedown handler. If you forgot it, no problem. But yeah, it's good to know the reason behind its inclusion in our case.

Calling preventDefault() could otherwise also have undesired consequences. For example, if there is a text input, then with preventDefault() in mousedown, we won't be able to focus on that input. Once again, remember that this exercise is only meant to test your understanding of mouse events, not to act as a practical, well-tested program to make the mouse interaction visible on the screen.

Last, but not the least, we'll add the circle <div> to the DOM via the appendChild() method of document.body:

/* ... */

document.body.appendChild(circleElement);

And we're done.

Let's test the program

Live Example

Thinking about usability

This exercise puts forward a superb idea of being able to distinguish between different mouse events when only having access to one's screen. But there's a slight issue with the idea of doing so with the help of an actual HTML element.

Let's see what...

Open up the link to the example above, and try clicking the link at the bottom-right corner that contains our logo. What happens? Are you able to open up the link? No, right?

You might think that this is because of the invocation of preventDefault() inside the handler of mousedown, but that's not the case. It's very easy to confirm this. Go ahead, comment out the line containing the call to preventDefault(), and then reload the webpage. Now, as before, try clicking on the link. Still, the click won't work!

So what exactly is the reason of this problem?

Well, it's because as soon as the circle <div> shows up on mousedown, the target of almost every subsequent mouse event becomes that <div> element. And that's simply by virtue of the fact that, at all times, the element right below the mouse pointer is the circle, the <div>.

Yes the circle element does get hidden upon mouseup, but strangely, the subsequently firing click event doesn't recognize the link (that you click) as its target and so the activation behavior for the link never ever gets triggered.

In short, the problem is in the circle shown right beneath the pointer.

It interferes with existing functionality of our example webpage. Now, as you would agree, we won't be concerned with this a lot while we are just implementing the idea in a standalone webpage.

However, if we want to integrate it into an existing application, we need to put a little bit more insight into usability and user experience. We clearly do NOT want to interfere with any existing functionality of the application because of our mouse event handlers!

And the first decision in that direction is to refrain from using an actual HTML element, for reasons mentioned before.

But if we can't use an HTML element, how can we make the mouse interactions visible on the screen?

Well, we could, all thanks to the CSS cursor property.

The idea is as follows: we work with the cursor of the document and change it to given images (that we have created already using some graphic-designing software) as we interact differently with the mouse. In that way, it would be the pointer changing itself, not any HTML element right beneath it, thus leading to absolutely no usability issues in the application.

How does that sound?

"I created Codeguage to save you from falling into the same learning conundrums that I fell into."

— Bilal Adnan, Founder of Codeguage