Instantly share code, notes, and snippets.
Created
December 5, 2008 17:31
-
Save edvakf/32414 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name spacnav-highlight.js | |
// @description Highlight the targets of spacial navigation | |
// @namespace http://d.hatena.ne.jp/edvakf/ | |
// @include http://* | |
// ==/UserScript== | |
(function(){ | |
var keyHash = { | |
16 : 'shift', | |
37 : 'left', | |
38 : 'up', | |
39 : 'right', | |
40 : 'down' | |
} | |
var moveFocusFunc = { | |
'up' : document.moveFocusUp, | |
'down' : document.moveFocusDown, | |
'left' : document.moveFocusLeft, | |
'right' : document.moveFocusRight, | |
} | |
function moveFocusTo(target){ | |
/* if target is a string call moveFocus(Up|Down|Left|Right) | |
* THE FUNCTIONS RETURN FALSE IF THERE IS NO TARGET TO MOVE | |
*/ | |
if(typeof(target)== 'string') return moveFocusFunc[target].call(document); | |
/* otherwise assume target is a DOM element */ | |
if(!target.id) target.id = 'move-focus-to'; | |
var origin = document.activeElement; | |
var style = origin.style; | |
var origNavRight = style.navRight; | |
style.navRight = '#'+target.id; | |
document.moveFocusRight(); | |
if(document.activeElement==document.body)document.body.blur(); | |
style.navRight = origNavRight; | |
if(target.id == 'move-focus-to') target.removeAttribute('id'); | |
} | |
function highlightNext(theElm){ | |
var elmSet = {}; /* cache of 4 surrounding elements */ | |
var origBGSet = {}; /* cache of original background color of 4 elements */ | |
function addStyle(dir){ | |
style = elmSet[dir].style; | |
if(style.backgroundColor) | |
origBGSet[dir]=style.backgroundColor; | |
style.backgroundColor = 'pink'; | |
} | |
function removeStyle(dir){ | |
if(!elmSet[dir])return; | |
style = elmSet[dir].style; | |
if(origBGSet[dir]) | |
style.backgroundColor=origBGSet[dir]; | |
else | |
style.backgroundColor = ''; | |
} | |
function highlight(dir){ | |
if(!moveFocusTo(dir)) return; | |
elmSet[dir] = document.activeElement; | |
moveFocusTo(theElm); | |
addStyle(dir); | |
} | |
/* highlight surrounding elements */ | |
['left','up','right','down'].forEach(highlight); | |
/* when arrows are pressed, move focus accordingly and highlight next */ | |
function keyPressHandler(e){ | |
var dir = keyHash[e.keyCode]; | |
if(!dir || dir=='shift' || !e.shiftKey)return; | |
if(!moveFocusTo(dir))return; /* move focus, or abort if failed */ | |
['down','right','up','left'].forEach(removeStyle); | |
/* remove styles in a backward order to adding them | |
* otherwise cache handling fails | |
*/ | |
var target = document.activeElement; | |
if(!target)return; | |
delete elmSet; | |
delete origBGSet; | |
highlightNext(target); | |
e.preventDefault(); /* this cancels the default spacial navigation */ | |
document.removeEventListener('keypress',arguments.callee,false); | |
} | |
document.addEventListener('keypress',keyPressHandler,false); | |
document.addEventListener('keyup',function(e){ | |
if( keyHash[e.keyCode] !='shift' ) return; | |
['down','right','up','left'].forEach(removeStyle); | |
if(origId)theElm.id = origId; | |
else theElm.removeAttribute('id'); | |
document.removeEventListener('keypress',keyPressHandler,false); | |
document.removeEventListener('keyup',arguments.callee,false); | |
delete elmSet; | |
delete origBGSet; | |
},false); | |
} | |
document.addEventListener('keydown',function(e){ | |
if(keyHash[e.keyCode]!='shift')return; | |
var currentElm = document.activeElement; | |
var inputReady = false; | |
if(/^(textarea|input)$/i.test(currentElm.tagName)){ | |
/* if element is textarea/input, differentiate "pre-focus" and "focus" */ | |
inputReady = true; | |
currentElm.addEventListener('focus',focusHandler,false); | |
currentElm.focus(); | |
if(!inputReady){ | |
/* if focus event is caught, make sure element is unfocused and | |
* it's back to "pre-focus" state | |
*/ | |
moveFocusTo(currentElm); | |
} | |
} | |
function focusHandler(e){ | |
e.preventDefault(); /* if focus event is caught, cancel any */ | |
e.stopPropagation(); /* actions associated with it */ | |
inputReady = false; | |
} | |
/* if focus event is not caught in 0.2 sec, assume textarea/input was input-ready */ | |
setTimeout(function(){ | |
if(inputReady)return; | |
if(document.activeElement != currentElm)return; | |
currentElm.removeEventListener('focus',focusHandler,false); | |
highlightNext(currentElm); | |
},200); | |
delete currentElm; | |
},false); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment