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:


So how to build that?
The task of building a tool like that is actually pretty easy:
Then I thought that I should be able to move the highlight on the page. For this I needed a bit more sophistication:
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().
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.
September 30, 2008 at 2:41 pm
Christian, most people with those big display’s don’t full screen the browser. Not only does it make websites way too wide, it can also make sites with relative width’s end up being wide enough to make it challenging to scan from one line to the next, hence why newspapers have very narrow columns. Typical sites are build around 1024 pixels wide, so if you set your browser at a more realistic width, and then make your desktop a darker color you’d be golden. But anyway, nice job on the bookmarklet, those are always very cool to see.
September 30, 2008 at 3:33 pm
That just solved one of my problem. Thanks!
I prefer to differ from “SeanBlader” because its not just about big screen size. I really feel the pain in my eyes when I read a lot of webpages in their white background. I just used this for a while and I can see a huge difference.
One small suggestion, I think the blinds should get disabled when you’re in a text area, I just experienced it going on and off when I typed this comment ;)
September 30, 2008 at 9:28 pm
Manoj, good catch! This is really annoying. Tricky though. I guess I can turn off the keyboard detect while you are in a form field.
October 1, 2008 at 4:58 am
@manoy the easiest is to change the code for showing and hiding the blinds to ESC instead of b. The change is:
var keyspy = new YAHOO.util.KeyListener(document,
{ keys:27 },
{ fn:peekaboo }
);
October 1, 2008 at 2:01 pm
Very nice. Thanks for this. Also, changing the opacity using the keyboard would be handy.
October 3, 2008 at 9:01 am
[...] If you’ve ever had trouble concentrating on a piece of text in a vast sea of white space, try closing the blinds, using some awesome YUI magic. Chris Heilmann goes into meticulously gory detail in the linked [...]
October 4, 2008 at 11:01 pm
[...] 地址在这里:reading blinds [...]
October 7, 2008 at 2:13 am
[...] 地址在这里:reading blinds [...]
October 7, 2008 at 3:49 pm
On a Mac try pressing ctrl-alt-apple-8 and the screen colours invert making reading much easier on the eyes.
April 13, 2009 at 8:34 pm
I am using Vimperator, an add-in for Firefox. To pass keys from Vimperator, I need to enable its “PASS THROUGH” mode. Esc key is for quitting the mode. Therefore, Esc key is not the option.
Actually, just refreshing the web page will remove the injected scripts reading blinds codes.