Friday, February 15, 2013

Scrolling Messages in the Scoreboard

‹prev | My Chain | next›

I am in a bit of a quandary with the scoreboard that I am building for 3D Game Programming for Kids. On the one hand, it allows me to not inflict DOM programming on unsuspecting beginners. On the other hand, DOM coding makes for an easy way to write ugly code that can be juxtaposed with cleaner code.

For instance, I introduce functions by having the reader add text to a log DIV element:
message = document.createElement('div');
message.textContent = 'My name is Chris.';
log.appendChild(message);

message = document.createElement('div');
message.textContent = 'I like popcorn.';
log.appendChild(message);
// ...
Eventually, I have readers define a message() function that performs the createElement() and appendChild() nonsense, leaving just the message being logged as the argument. Even a beginner can appreciate the beauty of a function when compared to DOM coding.

I do not know how I will replace DOM coding's ugliness in the narrative. But the remainder of the discussion relies on being able to do something that the Scoreboard cannot—appending messages. So I add an appendMessage() method:
Scoreboard.prototype.appendMessage = function(message) {
  function div(s) { return '<div>' + s + '</div>'; }
  this.setMessage((this._message || '') + div(message));
  return this;
};
Actually, I am not convinced that "append" is a useful term for beginners, so I create a couple of aliases:
Scoreboard.prototype.addMessage = Scoreboard.prototype.appendMessage;
Scoreboard.prototype.message = Scoreboard.prototype.appendMessage;
With that, I can append any number of messages to the board:
var scoreboard = new Scoreboard();
  scoreboard.
    appendMessage("Test 1").
    appendMessage("Test 2").
    appendMessage("Test 3").
    message("Test 4").
    addMessage("Test 5").
    message("Test 6");
I do not think it a good idea for the Scoreboard to grow vertically indefinitely. I introduce scrolling by specifying a maxHeight:
  var message_el = this.message_el = document.createElement('div');
  message_el.style.fontWeight = 'normal';
  message_el.style.color = 'white';
  message_el.style.maxHeight = (window.innerHeight * 0.2) + "px";
  message_el.style.overflowY = 'auto';
  el.appendChild(message_el);
It is so nice coding for a single, modern browser (the book only supports Chrome).

To ensure that the most recent message is always visible, I scroll then message element whenever it is updated:
Scoreboard.prototype.setMessage = function(message) {
  this.showMessage();
  this._message = message;
  this.message_el.innerHTML = this._message;

  this.message_el.scrollTop = this.message_el.scrollHeight;

  return this;
};
That seems to work quite well:



I make each of the scoreboard methods chainable because I appreciate being able to chain everything:
  var scoreboard = new Scoreboard();
  scoreboard.
    timer().
    countdown(50).
    help('Be excellent to each other.').
    appendMessage("Test 1").
    appendMessage("Test 2").
    appendMessage("Test 3").
    message("Test 4").
    addMessage("Test 5").
    message("Test 6");
I doubt that I will tell readers to code like that since it could distract from the topic at hand. Still, it is nice to have the option.

Regardless of the style of invoking those methods, I do think that I prefer using methods like this rather than setting these values in the constructor as I had been doing. The various constructor options were sure to confuse readers and the resulting logic was confusing me. These individual methods are much easier on both accounts.

I think that I am nearly done with this scoreboard class. Tomorrow, I will hook it back into the most recent game from the book to ensure that I am not overlooking anything.



Day #663

No comments:

Post a Comment