Search code examples
jquerykeyboard-eventsarrow-keys

jQuery 2.1.0 | List navigation with arrow keys


I am navigating within my app with the help of keyboard arrow keys. In my app, some dynamically set elements display vertically within a list. So far, the code below allows for horizontal navigation within each dynamically set <li> and vertical navigation throughout all dynamically set <li>. But there's a glitch: I can't seem to navigate vertically throughout all available links (element target focus is not a destination link).

See FIDDLE for demo.

IMPORTANT: Your solution must provide code that is oblivious to the amount of elements or their type, or their classes, within each <li>. Therefore, to keep code minimal and flexible, your solution must only refer to the index position of the source element that is focused, and target a destination element that has the same index as source (source element index greater than available destination element index must target last destination element) :

<ul>
<li><div class="card"><a href="javascript:void(0)" class="card-title">My Card</a><button class="card-export">CE</button><button class="vc-target">CT</button></div></li>
<li><div class="card"><a href="javascript:void(0)" class="card-title">My Card</a><button class="card-export">CE</button><button class="vc-target">CT</button></div></li>
<li><div class="card"><a href="javascript:void(0)" class="card-title">My Card</a><button class="card-export">CE</button><button class="vc-target">CT</button></div></li>
</ul>


$(function(){

$('.card').on('keydown', function(e){

var isfocus = $(this).find('a:focus,button:focus');
var isfocusindex = $(this).find('a:focus,button:focus').index()-1;
var afocus = $(this).find('a:focus');
var bfocuslast = $(this).find('button:last:focus');

if ( e.which == 37 ) { // Left arrowkey
isfocus.prev('button,a').focus();
afocus.parent('div').find('button:last').focus();
}
if ( e.which == 39 ) { // right arrowkey
isfocus.next('button').focus();
bfocuslast.parent('div').find('a:first').focus();
}

if ( e.which == 40 ) { // down arrowkey 
isfocus.parent('div').parent('li').next('li').find('div').find('a:eq('+isfocusindex+'),button:eq('+isfocusindex+')').focus();
}
if ( e.which == 38 ) { // UP arrowkey 
isfocus.parent('div').parent('li').prev('li').find('div').find('a:eq('+isfocusindex+'),button:eq('+isfocusindex+')').focus();
}

});

});

Solution

  • Here's a working solution that solved my issue. I coded more verbose than not, for sample usage :

    WORKING FIDDLE DEMO

    CSS (For demo purposes)

    a:focus, button:focus {color:red}
    a, button {width:55px;}
    UL{display:inline-block}
    ul li{margin-bottom:10px}
    div > a, button{margin-left:20px;}
    

    HTML

    <ul>
    <li><div class="card"><button class="card-export">CE</button><button class="vc-target">CT</button></div></li>
    <li><div class="card"><a href="javascript:void(0)" class="card-title">My Card</a><button class="card-export">CE</button><button class="vc-target">CT</button></div></li>
    <li><div class="card"><button class="card-export">CE</button><a href="javascript:void(0)" class="card-title">My Card</a><button class="vc-target">CT</button><button class="vc-target">CT</button><button class="vc-target">CT</button></div></li>
    <li><div class="card"><button class="card-export">CE</button><button class="vc-target">CT</button><a href="javascript:void(0)" class="card-title">My Card</a></div></li>
    <li><div class="card"><button class="card-export">CE</button><button class="vc-target">CT</button></div></li>
    </ul>
    

    JQUERY

    $(function(){
    
    $('.card').on('keydown', function(e){
    
    var isfocus = $(this).find(':focus');
    var isfocusindex = $(this).find(':focus').index();
    var isfocusbegin = $(this).find('*:first:focus');
    var isfocuslast = $(this).find('*:last:focus');
    var isnexttarget = isfocus.parent('div').parent('li').next('li').find('div').children(':last').index();
    var isprevtarget = isfocus.parent('div').parent('li').prev('li').find('div').children(':last').index();
    var isindexsource = isfocus.index();
    
    if ( e.which == 37 ) { // Left arrowkey
    isfocus.prev().focus();
    isfocusbegin.siblings(':last').focus();
    }
    if ( e.which == 39 ) { // right arrowkey
    isfocus.next().focus();
    isfocuslast.siblings(':first').focus();
    }
    
    if ( e.which == 40 ) { // down arrowkey
    isfocus.parent('div').parent('li').next('li').find('div').children(':eq('+isfocusindex+')').focus();
    if ( isindexsource > isnexttarget ) {
    isfocus.parent('div').parent('li').next('li').find('div').children(':last').focus();        
    }   
    }  
    if ( e.which == 38 ) { // UP arrowkey
    isfocus.parent('div').parent('li').prev('li').find('div').children(':eq('+isfocusindex+')').focus();
    if ( isindexsource > isprevtarget ) {
    isfocus.parent('div').parent('li').prev('li').find('div').children(':last').focus();        
    }
    }
    
    });
    
    });