Mark
Mark

Reputation: 323

Cycling through a jQuery object with elements grouped together

I have a set of list items that contain nested lists, sort of like this:

<ul class="searchselectors">
  <li class="group">Group name 1
    <ul>
      <li class="item selected">List item 1.1</li>
      <li class="item">List item 1.2</li>
      <li class="item">List item 1.3</li>
    </ul>
  </li>
  <li class="group">Group name 2
    <ul>
      <li class="item">List item 2.1</li>
      <li class="item">List item 2.2</li>
      <li class="item">List item 2.3</li>
    </ul>
  </li>
  <li class="group">Group name 3
    <ul>
      <li class="item">List item 3.1</li>
    </ul>
  </li>
</ul>

I want to cycle through all of the .item elements using up/down arrow keys (which I already have set up by using on('keydown') and catching key codes 38 and 40) and set .selected on the next item before or after the currently selected item, wrapping around to the top/bottom as necessary.

Using $().next() and $().prev() will not work, since it will only work on siblings, and not on a jQuery object such as $('.searchselectors .item').\

Upvotes: 1

Views: 157

Answers (4)

webketje
webketje

Reputation: 10976

In my quest to provide native JS answers to those looking for it, here is @Mario j Vargas' good answer adapted in native Javascript. It only takes 2 lines of extra code.

http://jsfiddle.net/kevinvanlierde/7tQSW/2/

Only putting the JS up here, HTML is the same.

(function () {
    var $menu = document.getElementById('searchselectors'),
        items = $menu.getElementsByClassName('item'),
        $selectedItem = $menu.getElementsByClassName('selected')[0],
        selectedIndex = Array.prototype.indexOf.call($selectedItem, items)+1;

    document.body.addEventListener('keydown', function (e) {
        switch(e.keyCode) {
            case 40:  // down arrow
                $selectedItem.className = $selectedItem.className.replace(' selected','');
                selectedIndex = selectedIndex < items.length - 1 ? selectedIndex + 1 : selectedIndex;
                $selectedItem = items[selectedIndex];
                $selectedItem.className += ' selected';
                break;
            case 38:  // up arrow
                $selectedItem.className = $selectedItem.className.replace(' selected','');
                selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : selectedIndex;
                $selectedItem = items[selectedIndex];
                $selectedItem.className += ' selected';
                break;
        }
    }, false);
}());

Upvotes: 1

Mario J Vargas
Mario J Vargas

Reputation: 1195

I was working on the same problem but in my project I'm using KnockoutJS. In fact, the original logic was written with pure jQuery and I refactored it with Knockout. Here's a solution for your problem using jQuery:

Fiddle - http://jsfiddle.net/6QN77/2/

I didn't spend too much time cleaning up the JavaScript, but I'm leaving that to you now.

HTML

<ul id="searchselectors">
  <li class="group">Group name 1
    <ul>
      <li class="item selected">List item 1.1</li>
      <li class="item">List item 1.2</li>
      <li class="item">List item 1.3</li>
    </ul>
  </li>
  <li class="group">Group name 2
    <ul>
      <li class="item">List item 2.1</li>
      <li class="item">List item 2.2</li>
      <li class="item">List item 2.3</li>
    </ul>
  </li>
  <li class="group">Group name 3
    <ul>
      <li class="item">List item 3.1</li>
    </ul>
  </li>
</ul>

jQuery

$(function () {
    var $menu = $("#searchselectors"),
        $items = $menu.find(".item"),
        $selectedItem = $menu.find(".selected"),
        selectedIndex = $selectedItem.length - 1;

    $(document).on("keydown", function (e) {
        switch(e.keyCode) {
            case 40:  // down arrow
                $selectedItem.removeClass("selected");
                selectedIndex = (selectedIndex + 1) % $items.length;
                $selectedItem = $items.eq(selectedIndex).addClass("selected");
                break;
            case 38:  // up arrow
                $selectedItem.removeClass("selected");
                selectedIndex = (selectedIndex - 1) % $items.length;
                $selectedItem = $items.eq(selectedIndex).addClass("selected");
                break;
        }
    });
});

UPDATE: My solution revolves around getting a reference to a wrapper element (#searchselectors) and getting all the LI elements marked with the CSS class .item. Next I get a reference to the currently selected element and its index. Finally, the code listens to the down and up arrow keys being pressed (keydown), decrementing if going up and incrementing if going up. Cycling is achieved via the modulus operator. The selected item's CSS class is removed and put back. The selected item reference is used for performance reasons so I don't have to write $items.removeClass(".selected").eq(selectedIndex).addClass("selected");

Upvotes: 4

Ashima
Ashima

Reputation: 4824

Easiest way to get all the list items with class "item" is:

var items = $('.item'); 

 for (var i = 0; i < items.length; i++) {
      var item = items[i]; // iterate
 }

And then, you can select the next item from the list

OR

you can do this

 if (!$().next) {
    var nextUL= $().parent.find('ul')
    iterateThruList(nextUL);
 }

Upvotes: 0

AndiFaria
AndiFaria

Reputation: 27

You could use something like

var UL = $(".searchselectors").children().length; //number of UL (group names)

to iterate through items.

Upvotes: -1

Related Questions