DOM  Time

DOM Time

KeyDown events, 2 keys at a time, moving a div, and a moving one-act about moving a div

·

8 min read


Prologue

My name is Matt and this is my Blog. If you made it to this, the second, sentence I appreciate it. I don't feel good about the first one but I haven't figured out how to backspace yet. That's because I'm new to blogging.

I'm also new to the world of coding - which is the main focus of this blog. Double Whammy.

If it's bad form to lead with multiple disclosures concerning my inexperience then I apologize and I'll learn to be better. This post is but the acorn to a one-day mighty oak of a blog.

Chapter 1 - "The Prologue Continues"

I'm currently attending the Flatiron School's software engineering bootcamp. The program lasts 15 weeks, divided into 5 three-week phases. As I write this we're at the tail end of phase-1 which has focused on JavaScript...vanilla JavaScript. I took the Flatiron website at its word when it said no coding experience needed. The process has been rewarding, but the volume of new concepts and vocab combined with the pace at which it's all taught can be overwhelming. That being the case, it's strategically misguided to dwell on the labs/lessons of lesser immediate relevance to the bigger conceptual picture of the phase, leaving some labs of interest shrinking in the rear-view mirror.

The most successful metaphors are the ones that span multiple paragraphs and one of the labs I felt like pulling over for was a very brief intro to the use of events in controlling the movement of elements on the page. Below is a very slightly less brief intro.

Chapter 2 - "The First and Only Chapter"

To paraphrase the lab, below is a slightly modified function and set of variables provided as a starting point:

const ball = document.querySelector("#ball");
ball.style.bottom = "0px";
ball.style.left = "145px";

function moveBallLeft() {
  const leftNumbers = ball.style.left.replace("px", "");
  const left = parseInt(leftNumbers, 10);
  if (left > 0) {
    ball.style.left = `${left - 1}px`;
  }
}

Following the "if" you can see the function cause a "ball" to move left by 1 pixel as long as its left position is greater than 0. When the ball's left position reaches 0 it can no longer continue with its leftward motion creating the effect that the ball has hit the lefthand wall. An eventListener with a keydown event invokes the function when the ArrowLeft key is pressed:

document.addEventListener("keydown", function (e) {
  if (e.key === "ArrowLeft") {
    moveBallLeft();
  }
});

When combined with the corresponding moveBallRight( ) code (and a little styling) produces:

From there I wanted to add up function and a down function, with movement in all four directions still contained within the four walls of its parent div (the '#game" div).

As an aside, after finding this helpful video, I applied the demonstrated switch statement to my scenario which yielded the following:

const moveTen = 10;

document.addEventListener('keydown', (e) => {
  switch(e.key) {
    case 'ArrowLeft':
      ball.style.left = parseInt(ball.style.left) - moveTen + 'px';
      break;
    case 'ArrowRight':
      ball.style.left = parseInt(ball.style.left) + moveTen + 'px';
      break;
    case 'ArrowUp':
      ball.style.bottom = parseInt(ball.style.bottom) + moveTen + 'px';
      break;
    case 'ArrowDown':
      ball.style.bottom = parseInt(ball.style.bottom) - moveTen + 'px';
      break
    };
  });

This was great as an efficient way to achieve the up/down/left/right movement goal by attaching all directions to a single eventListener and I imagine it would come in handy for the right purpose:

(If you tried out the example above you might have noticed that in addition to moving up and down this version's ball also moves faster. It's 10x faster. This is because I set the value of moveTen at 10 rather than 1 pix. The next sample will move twice as fast as the last one. please tool around in the resources to alter speed, appearance, whatever.)

The switch statment above omits the functionality of the "if" statements that kept the ball within the confines the "game" div. Accounting for the missing "if"s already made the whole thing seem needlessly bulky and there are more features still to add so i went back to splitting things up. I replaces the moveBallLeft( )/moveBallRight( ) functions and used the same formula to add moveBallUp( ) and moveBallDown( ) as well as the corresponding events/listeners for each:

With that all in place I wanted to combine multiple keys being pressed to act as a single keydown event, invoking another directional movement function. For example, when the ArrowUp and ArrowLeft keys are pressed simultaneously the ball will move diagonally up+left.

It turns out this is a slightly more complex process than I'd originally imagined. My first approach involved some "&&"s in an attempt to mash together combinations of established functions and eventListeners:

function moveBallUpLeft() {
  const bottomNumbers = ball.style.bottom.replace("px", "");
  const bottom = parseInt(bottomNumbers, 10);
  const leftNumbers = ball.style.left.replace("px", "");
  const left = parseInt(leftNumbers, 10);

  if (left > 0 && bottom < 275) {
    ball.style.left = `${left - 1}px`;
    ball.style.bottom = `${bottom + 10}px`;
  };
};

document.addEventListener("keydown", function (e) {
  if (e.key === "ArrowLeft"&&"ArrowUp") {
    moveBallUpLeft();
  };
});

This did exactly what I wanted so I repeated the pattern for the other diagonal directions but when I tested it out everything was a totally out of control mess. The moveBallUpLeft() function still worked but none of the other diagonals did and even pressing ArrowLeft by itself now send the ball in the same direction as moveBallUpLeft:

After a few more faulty variations of my own and some failed attempts guided by stackoverflow (most likely due to a fundamental misunderstanding of the guidance on my part) I came across this entry on GavsBlog.

The method demonstrated by Gav begins by creating an empty object. When pressed one of the (keyboard) keys is assigned as the object's key which has a value of "true". Even I'm confused by how I explained that. Here's the code:

let diagKeys = {};

document.addEventListener('keydown', (e) => {
  diagKeys[e.key] = true;

And, keeping the previously functioning moveBallUpLeft( ) here's the specific code written for the up+left movement:

let diagKeys = {};

function moveBallUpLeft() {
  const bottomNumbers = ball.style.bottom.replace("px", "");
  const bottom = parseInt(bottomNumbers, 10);
  const leftNumbers = ball.style.left.replace("px", "");
  const left = parseInt(leftNumbers, 10);

  if (left > 0 && bottom < 275) {
    ball.style.left = `${left - 1}px`;
    ball.style.bottom = `${bottom + 10}px`;
  }
}

document.addEventListener('keydown', (e) => {
  diagKeys[e.key] = true;
  if (diagKeys['ArrowUp'] && e.key == 'ArrowLeft') {
    moveBallUpLeft();
  }
});

As you can see the two (keyboard) keys still come together with an "&&" much like they did in the earlier example that turned everything into a mess.

That's all fixed with the following keyup event/listener that cleans the diagKeys slate by deleting the previously assigned ArrowUp (object) key:

document.addEventListener('keyup', (e) => {
  delete diagKeys[e.key];
});

The result is no lingering effects from previously fired events to cause interference when the next combo of keys is pressed:

And with that, this post's deliverables have been completed. There are a number of untouched stretch deliverables (I'd like a tweak the code for a version in which the ball exits screen right and re-enters screen left. I'd also like to play around with the keyup event and add some functionality there - like having the ball slow to a halt on the keyup rather than the current abrupt stop.) That all will have to wait.

The End


Epilogue

I planned on closing with some observations that might - if only a little bit - convey the nature of the interaction between HTML, CSS, and Javascript but I think this one-act play written by ChatGPT might be more effective. Please enjoy...

Title: The Web Trio

Cast:

  • JavaScript, an enthusiastic and energetic character.

  • HTML, a pragmatic and grounded character.

  • CSS, a stylish and detail-oriented character.

Setting: A blank stage, with a table and chairs on one side and a computer on the other.

Act 1:

(As the lights come up, the three characters enter from stage left. They take their places around the table.)

JavaScript: (Excitedly) Okay, team! We have a new task to accomplish. We need to create a div element that can be moved using the arrow keys.

HTML: (Nods) Sounds simple enough. What do you need me to do?

CSS: (Chuckles) Simple? You do realize we're dealing with user interface design here, HTML. I'll be taking care of the styling and layout.

JavaScript: (Grinning) And I'll be taking care of the behavior and interaction. Let's get started!

(They turn to the computer and begin typing away. HTML starts by creating the div element, while CSS adds some styling to make it stand out.)

CSS: (Muttering to himself) Let's see, some padding, a background color, and a border should do the trick.

HTML: (Pausing) Done. Now what?

JavaScript: (Smiling) Now we add the event listeners for the arrow keys. When a key is pressed, we'll move the div accordingly.

(JavaScript adds the necessary code, and they all take a step back to admire their work.)

HTML: (Proudly) It looks great! Nice job, CSS.

CSS: (Grinning) Thanks, HTML. Your code was solid as usual.

JavaScript: (Beaming) And we couldn't have done it without the smooth cooperation of our team. (Looks at the audience) And that, ladies and gentlemen, is how you render a div element that can be moved using the arrow keys.

(They take their bows as the curtain falls.)

The end.