Reading Blinds — a YUI-powered Reading Tool

September 30, 2008 at 12:57 pm by Christian Heilmann | In Accessibility, Development | 9 Comments

Ever since I got upgraded to a shiny Macbook Pro and a 24 inch monitor at work I had a web experience that differed a lot from what I had before. Web sites that were easy and nice to read out of a sudden showed a massive amount of white space that actually hurt my eyes. Talking to several people with visual impairments and dyslexia at Scripting Enabled confirmed me that this can be a real issue.

This is why I thought of writing a small script that can be used as a bookmarklet to cover the screen with a dark overlay and only shows a few lines at a time. That way you can concentrate on the bit you are reading at the moment and the rest of the screen does not bother you too much.

Following are two screenshots of the same site with and without reading blinds:

Browser with Reading Blinds

Browser without Reading Blinds

So how to build that?

The task of building a tool like that is actually pretty easy:

  • create two DIVs with black background and 85% opacity
  • position them fixed to the top and the bottom of the screen
  • set their height to 10% and 70% to leave a gap

Then I thought that I should be able to move the highlight on the page. For this I needed a bit more sophistication:

  • Detect the mouse cursor position
  • Make the top div span from the current top of the document to a few pixels above the cursor position
  • Make the bottom div span from a few pixels below the current cursor position to the bottom of the viewport

I could have had a go at it myself, but I don’t want to end in browser inconsistency hell, hence I use YUI.

Here’s the code:

var readingblinds = function(){
  var visible = true;
  var y,top,bottom;
  var info = true;
  var size = 70;
  function generate(){
    top = document.createElement('div');
    bottom = document.createElement('div');
    document.body.appendChild(top);
    document.body.appendChild(bottom);
    styleTopBottom();
    var message = document.createElement('div');
    var note = document.createTextNode(
      'Reading Blinds - ' + 
      'move mouse to highlight section to read. ' 
    );
    message.appendChild(note);
    top.appendChild(message);
    styleMessage(message);
    YAHOO.util.Event.on(document, "mousemove", move);
  };
  function move(e){
    y = YAHOO.util.Event.getXY(e);
    if(y[1] > size){
      render(y);
    }
  };
  function render(y){
    var real = y[1]-YAHOO.util.Dom.getDocumentScrollTop();
    YAHOO.util.Dom.setStyle(top,'height',real-size+'px');
    var h = YAHOO.util.Dom.getViewportHeight()-real+size;    
    YAHOO.util.Dom.setStyle(bottom,'top',real+size+'px');
    YAHOO.util.Dom.setStyle(bottom,'height',h + 'px');
  };
  function styleMessage(message){
    YAHOO.util.Dom.setStyle(message,'font-size','80%');
    YAHOO.util.Dom.setStyle(message,'text-align','right');
    YAHOO.util.Dom.setStyle(message,'padding','5px');
    YAHOO.util.Dom.setStyle(message,'font-family','verdana,sans-serif');
    YAHOO.util.Dom.setStyle(message,'color','white');
  }
  function styleTopBottom(){
    YAHOO.util.Dom.batch([top,bottom],function(o){
      YAHOO.util.Dom.setStyle(o,'background','#000');
      YAHOO.util.Dom.setStyle(o,'width','100%');
      YAHOO.util.Dom.setStyle(o,'position','fixed');
      YAHOO.util.Dom.setStyle(o,'left','0');
      YAHOO.util.Dom.setStyle(o,'height','10%');
      YAHOO.util.Dom.setStyle(o,'opacity','.85');
      YAHOO.util.Dom.setStyle(o,'overflow','hidden');
    });
    YAHOO.util.Dom.setStyle(top,'top','0');
    YAHOO.util.Dom.setStyle(bottom,'bottom',0);
    YAHOO.util.Dom.setStyle(bottom,'height','70%');
  };
  return{
    init:generate
  }
}();
readingblinds.init();

The interesting methods are move() and render(); the rest is more or less run-of-the-mill DOM scripting.

The move() method is an event handler that gets called by any mousemove event on the document. YUI’s Dom Collection then makes it easy for me to get the current mouse cursor position with getXY() and I just need to make sure that the mouse is low enough in the browser window to not cause a negative height on the top div.

The render() method then sets the appropriate heights. I determine the upper border of the browser with getDocumentScrollTop() and substract that one from the cursor position. To determine where to end the bottom div I use getViewPortHeight().

Addding dazzle with keyboard controls

This was cool enough, but I wanted to be able to turn the blinds on and off and change the size of the visible part with the keyboard, too. For this, I needed to use the keylistener utility some tool methods to resize the gap or show and hide both of the cover divs. The resizing methods needed to get some boundaries to avoid div overlap or the whole viewport to be uncovered.

var readingblinds = function(){
  var visible = true;
  var y,top,bottom;
  var info = true;
  var size = 70;
  function generate(){
    top = document.createElement('div');
    bottom = document.createElement('div');
    document.body.appendChild(top);
    document.body.appendChild(bottom);
    styleTopBottom();
    var message = document.createElement('div');
    var note = document.createTextNode(
      'Reading Blinds - ' + 
      'move mouse to highlight section to read. ' +
      'Press "b" to show and hide, "s" to decrease size,' + 
      ' "l" to increase size'
    );
    message.appendChild(note);
    top.appendChild(message);
    styleMessage(message);
    var keyspy = new YAHOO.util.KeyListener(
      document, 
      { keys:66 }, 
      { fn:peekaboo }
    );
    keyspy.enable();
    var keyspy2 = new YAHOO.util.KeyListener(
      document, 
      { keys:83 }, 
      { fn:smaller }
    );
    keyspy2.enable();
    var keyspy3 = new YAHOO.util.KeyListener(
      document, 
      { keys:76 }, 
      { fn:larger }
    );
    keyspy3.enable();
    YAHOO.util.Event.on(document, "mousemove", move);
  };
  function move(e){
    y = YAHOO.util.Event.getXY(e);
    if(y[1] > size){
      render(y);
    }
  };
  function render(y){
    var real = y[1]-YAHOO.util.Dom.getDocumentScrollTop();
    YAHOO.util.Dom.setStyle(top,'height',real-size+'px');
    YAHOO.util.Dom.setStyle(bottom,'top',real+size+'px');
    var h = YAHOO.util.Dom.getViewportHeight()-real+size;    
    YAHOO.util.Dom.setStyle(bottom,'height',h + 'px');
  };
  function styleMessage(message){
    YAHOO.util.Dom.setStyle(message,'font-size','80%');
    YAHOO.util.Dom.setStyle(message,'text-align','right');
    YAHOO.util.Dom.setStyle(message,'padding','5px');
    YAHOO.util.Dom.setStyle(message,'font-family','verdana,sans-serif');
    YAHOO.util.Dom.setStyle(message,'color','white');
  }
  function styleTopBottom(){
    YAHOO.util.Dom.batch([top,bottom],function(o){
      YAHOO.util.Dom.setStyle(o,'background','#000');
      YAHOO.util.Dom.setStyle(o,'width','100%');
      YAHOO.util.Dom.setStyle(o,'position','fixed');
      YAHOO.util.Dom.setStyle(o,'left','0');
      YAHOO.util.Dom.setStyle(o,'height','10%');
      YAHOO.util.Dom.setStyle(o,'opacity','.85');
      YAHOO.util.Dom.setStyle(o,'overflow','hidden');
    });
    YAHOO.util.Dom.setStyle(top,'top','0');
    YAHOO.util.Dom.setStyle(bottom,'bottom',0);
    YAHOO.util.Dom.setStyle(bottom,'height','70%');
  };
  function peekaboo(){
    if(visible === true){
      YAHOO.util.Dom.setStyle(top,'display','none');
      YAHOO.util.Dom.setStyle(bottom,'display','none');
      visible = false;
    } else {
      YAHOO.util.Dom.setStyle(top,'display','block');
      YAHOO.util.Dom.setStyle(bottom,'display','block');
      visible = true;
    }
  };
  function smaller(){
    if(size > 10){
      size -= 5;
      render(y);
    }
  };
  function larger(){
    if(size <  YAHOO.util.Dom.getViewportHeight()/2){
      size += 5;
      render(y);
    }
  };
  return{
    init:generate
  }
}();
readingblinds.init();

That was pretty cool already, but as I wanted to make reading blinds a single script include or bookmarklet I had the problem of relying on the YUI. Well, there is a trick to conjure YUI from thin air by using the YAHOO_config object with the listener method creating a script node to get the YUI Loader.

So instead of calling readingblinds.init() directly, I used the following magic YUI trick:

if(typeof YAHOO=="undefined"||!YAHOO){
  YAHOO_config = function(){
    var s = document.createElement('script');
    s.setAttribute('type','text/javascript');
    s.setAttribute('src','http://yui.yahooapis.com/2.5.2/'+
                   'build/yuiloader/yuiloader-beta-min.js');
    document.getElementsByTagName('head')[0].appendChild(s);
    return{
      listener:function(o){
        if(o.name === 'get'){
          window.setTimeout(YAHOO_config.ready,1);
        }
      },
      ready:function(){
        var loader = new YAHOO.util.YUILoader();
        var dependencies = ['yahoo','dom','event'];
        loader.require(dependencies);
        loader.loadOptional = true;
        loader.insert({
          onSuccess:function(){
            readingblinds.init();
          }
        });
      }
    };
  }();
} else {
  readingblinds.init();
}

That’s the lot. You can download readingblinds.js and include it in your site, or you can drag the following link to your links toolbar: Reading Blinds.

Share and extend: Bookmark with Yahoo! My Web | Bookmark with del.icio.us | digg it! | reddit!

Free Chapter: “Coding and Design Patterns” from Stoyan Stefanov’s Object-Oriented JavaScript

September 26, 2008 at 7:29 am by Eric Miraglia | In Development | 7 Comments

Stoyan Stefanov's 'Object Oriented JavaScript' [book cover]Stoyan Stefanov is a member of Yahoo’s Exceptional Performance team; he’s worked on a variety of performance-related projects at Yahoo, including the popular YSlow plugin for Firebug. He’s also a contributing author here on YUIBlog.

Stoyan’s latest project is Object Oriented JavaScript, a new book from Packt whose simple goal is to help you learn how to “think in JavaScript.” Stoyan and his editors were kind enough to let us share with you Chapter 8 from OOJS — “Coding and Design Patterns.”

Among the coding patterns explored in this chapter:

  • Separating behavior
  • Namespaces
  • Init-time branching
  • Lazy definition
  • Configuration objects
  • Private variables and methods
  • Privileged methods
  • Private functions as public methods
  • Self-executable functions
  • Chaining
  • JSON

Design patterns include:

  • Singleton
  • Factory
  • Decorator
  • Observer

The sample chapter is an excellent standalone resource, but the full volume is outstanding as well. Our thanks to Stoyan and his editors for allowing us to freely share the coding and design patterns content here.

Share and extend: Bookmark with Yahoo! My Web | Bookmark with del.icio.us | digg it! | reddit!

Reminder: YUI 3.x Meetup Tonight and Yahoo! Live Channel Info

September 3, 2008 at 1:04 pm by Eric Miraglia | In Development | 3 Comments

If you can’t make it to the YUI meetup tonight but you do want to drop in and see what’s going on, we’ll be broadcasting the event on the experimental Yahoo! Live service:

Live is a relatively recent product out of Yahoo’s Brickhouse development incubator. We haven’t used it for this purpose before, but since we’ll be up at Brickhouse for tomorrow night’s meetup we thought we’d highlight some of that group’s fantastic work.

Hope to see you online or in person tonight!

Note: The Live channel will not be broadcasting all day — just from about 7:15 p.m. through the end of the code demos. Drop in after 7:15 p.m. PDT tonight and, barring any technical challenges, you should find us there.

Share and extend: Bookmark with Yahoo! My Web | Bookmark with del.icio.us | digg it! | reddit!

Another YUI-based Game from Kris Cieslak: Pacman

September 2, 2008 at 10:45 am by Eric Miraglia | In Development, In the Wild | 8 Comments

Visit Kris's new game at digitalinsane.com.Kris Cieslak is back, after a long break, with a new YUI-based game: Pacman. We last heard from Kris about 18 months ago, when he was showing off Yetris, Puzzle and Solitaire. Now Kris returns with another classic game built entirely in JavaScript, bolstered by YUI 2.5.2’s core (Yahoo, Dom and Event). Check it out and give Kris feedback on his blog.

Share and extend: Bookmark with Yahoo! My Web | Bookmark with del.icio.us | digg it! | reddit!

Hosted by Yahoo!

Copyright © 2007 Yahoo! Inc. All rights reserved. Privacy Policy - Terms of Service

Powered by WordPress on Yahoo! Web Hosting.