Клавиатурная навигация для вкладок jQuery - PullRequest
1 голос
/ 07 июня 2010

Как сделать Keyboard навигацией влево / вверх / вправо / вниз (как для фотогалереи) для вкладок jQuery с историей? Демо без функции клавиатуры в http://dl.dropbox.com/u/6594481/tabs/index.html

Необходимые функции:

  1. на клавиатуре top/down make select и CSS show active вложенные вкладки ajax с 1-го по последний уровень
  2. на клавиатуре left/right изменить back/forward содержимое active вкладка вложенных вкладок Ajax
  3. дополнительная опция, делает active вложенную вкладку AJAX в 'курсор на' на уровне конкретных вложенных AJAX вкладок

Подробнее с примерами фотографий читайте в https://stackoverflow.com/questions/2975003/jquery-tools-to-make-keyboard-and-cookies-feature-for-ajaxed-tabs-with-history

/**
 * @license 
 * jQuery Tools @VERSION Tabs- The basics of UI design.
 * 
 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
 * 
 * http://flowplayer.org/tools/tabs/
 *
 * Since: November 2008
 * Date: @DATE 
 */  
(function($) {

    // static constructs
    $.tools = $.tools || {version: '@VERSION'};

    $.tools.tabs = {

        conf: {
            tabs: 'a',
            current: 'current',
            onBeforeClick: null,
            onClick: null, 
            effect: 'default',
            initialIndex: 0,            
            event: 'click',
            rotate: false,

            // 1.2
            history: false
        },

        addEffect: function(name, fn) {
            effects[name] = fn;
        }

    };

    var effects = {

        // simple "toggle" effect
        'default': function(i, done) { 
            this.getPanes().hide().eq(i).show();
            done.call();
        }, 

        /*
            configuration:
                - fadeOutSpeed (positive value does "crossfading")
                - fadeInSpeed
        */
        fade: function(i, done) {        

            var conf = this.getConf(),            
                 speed = conf.fadeOutSpeed,
                 panes = this.getPanes();

            if (speed) {
                panes.fadeOut(speed);    
            } else {
                panes.hide();    
            }

            panes.eq(i).fadeIn(conf.fadeInSpeed, done);    
        },

        // for basic accordions
        slide: function(i, done) {
            this.getPanes().slideUp(200);
            this.getPanes().eq(i).slideDown(400, done);             
        }, 

        /**
         * AJAX effect
         */
        ajax: function(i, done)  {            
            this.getPanes().eq(0).load(this.getTabs().eq(i).attr("href"), done);    
        }        
    };       

    var w;

    /**
     * Horizontal accordion
     * 
     * @deprecated will be replaced with a more robust implementation
     */
    $.tools.tabs.addEffect("horizontal", function(i, done) {

        // store original width of a pane into memory
        if (!w) { w = this.getPanes().eq(0).width(); }

        // set current pane's width to zero
        this.getCurrentPane().animate({width: 0}, function() { $(this).hide(); });

        // grow opened pane to it's original width
        this.getPanes().eq(i).animate({width: w}, function() { 
            $(this).show();
            done.call();
        });

    });    


    function Tabs(root, paneSelector, conf) {

        var self = this, 
             trigger = root.add(this),
             tabs = root.find(conf.tabs),
             panes = paneSelector.jquery ? paneSelector : root.children(paneSelector),             
             current;


        // make sure tabs and panes are found
        if (!tabs.length)  { tabs = root.children(); }
        if (!panes.length) { panes = root.parent().find(paneSelector); }
        if (!panes.length) { panes = $(paneSelector); }


        // public methods
        $.extend(this, {                
            click: function(i, e) {

                var tab = tabs.eq(i);                                                 

                if (typeof i == 'string' && i.replace("#", "")) {
                    tab = tabs.filter("[href*=" + i.replace("#", "") + "]");
                    i = Math.max(tabs.index(tab), 0);
                }

                if (conf.rotate) {
                    var last = tabs.length -1; 
                    if (i < 0) { return self.click(last, e); }
                    if (i > last) { return self.click(0, e); }                        
                }

                if (!tab.length) {
                    if (current >= 0) { return self; }
                    i = conf.initialIndex;
                    tab = tabs.eq(i);
                }                

                // current tab is being clicked
                if (i === current) { return self; }

                // possibility to cancel click action                
                e = e || $.Event();
                e.type = "onBeforeClick";
                trigger.trigger(e, [i]);                
                if (e.isDefaultPrevented()) { return; }

                // call the effect
                effects[conf.effect].call(self, i, function() {

                    // onClick callback
                    e.type = "onClick";
                    trigger.trigger(e, [i]);                    
                });            

                // default behaviour
                current = i;
                tabs.removeClass(conf.current);    
                tab.addClass(conf.current);                

                return self;
            },

            getConf: function() {
                return conf;    
            },

            getTabs: function() {
                return tabs;    
            },

            getPanes: function() {
                return panes;    
            },

            getCurrentPane: function() {
                return panes.eq(current);    
            },

            getCurrentTab: function() {
                return tabs.eq(current);    
            },

            getIndex: function() {
                return current;    
            }, 

            next: function() {
                return self.click(current + 1);
            },

            prev: function() {
                return self.click(current - 1);    
            }        

        });

        // callbacks    
        $.each("onBeforeClick,onClick".split(","), function(i, name) {

            // configuration
            if ($.isFunction(conf[name])) {
                $(self).bind(name, conf[name]); 
            }

            // API
            self[name] = function(fn) {
                $(self).bind(name, fn);
                return self;    
            };
        });


        if (conf.history && $.fn.history) {
            $.tools.history.init(tabs);
            conf.event = 'history';
        }    

        // setup click actions for each tab
        tabs.each(function(i) {                 
            $(this).bind(conf.event, function(e) {
                self.click(i, e);
                return e.preventDefault();
            });            
        });

        // cross tab anchor link
        panes.find("a[href^=#]").click(function(e) {
            self.click($(this).attr("href"), e);        
        }); 

        // open initial tab
        if (location.hash) {
            self.click(location.hash);
        } else {
            if (conf.initialIndex === 0 || conf.initialIndex > 0) {
                self.click(conf.initialIndex);
            }
        }                

    }


    // jQuery plugin implementation
    $.fn.tabs = function(paneSelector, conf) {

        // return existing instance
        var el = this.data("tabs");
        if (el) { return el; }

        if ($.isFunction(conf)) {
            conf = {onBeforeClick: conf};
        }

        // setup conf
        conf = $.extend({}, $.tools.tabs.conf, conf);        

        this.each(function() {                
            el = new Tabs($(this), paneSelector, conf);
            $(this).data("tabs", el); 
        });        

        return conf.api ? el: this;        
    };        

}) (jQuery); 

/**
 * @license 
 * jQuery Tools @VERSION History "Back button for AJAX apps"
 * 
 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
 * 
 * http://flowplayer.org/tools/toolbox/history.html
 * 
 * Since: Mar 2010
 * Date: @DATE 
 */
(function($) {

    var hash, iframe, links, inited;        

    $.tools = $.tools || {version: '@VERSION'};

    $.tools.history = {

        init: function(els) {

            if (inited) { return; }

            // IE
            if ($.browser.msie && $.browser.version < '8') {

                // create iframe that is constantly checked for hash changes
                if (!iframe) {
                    iframe = $("<iframe/>").attr("src", "javascript:false;").hide().get(0);
                    $("body").append(iframe);

                    setInterval(function() {
                        var idoc = iframe.contentWindow.document, 
                             h = idoc.location.hash;

                        if (hash !== h) {                        
                            $.event.trigger("hash", h);
                        }
                    }, 100);

                    setIframeLocation(location.hash || '#');
                }


            // other browsers scans for location.hash changes directly without iframe hack
            } else { 
                setInterval(function() {
                    var h = location.hash;
                    if (h !== hash) {
                        $.event.trigger("hash", h);
                    }                        
                }, 100);
            }

            links = !links ? els : links.add(els);

            els.click(function(e) {
                var href = $(this).attr("href");
                if (iframe) { setIframeLocation(href); }

                // handle non-anchor links
                if (href.slice(0, 1) != "#") {
                    location.href = "#" + href;
                    return e.preventDefault();        
                }

            }); 

            inited = true;
        }    
    };  


    function setIframeLocation(h) {
        if (h) {
            var doc = iframe.contentWindow.document;
            doc.open().close();    
            doc.location.hash = h;
        }
    } 

    // global histroy change listener
    $(window).bind("hash", function(e, h)  { 
        if (h) {
            links.filter(function() {
              var href = $(this).attr("href");
              return href == h || href == h.replace("#", ""); 
            }).trigger("history", [h]);    
        } else {
            links.eq(0).trigger("history", [h]);    
        }

        hash = h;
    window.location.hash = hash;
    });


    // jQuery plugin implementation
    $.fn.history = function(fn) {

        $.tools.history.init(this);

        // return jQuery
        return this.bind("history", fn);        
    };    

})(jQuery);
$(function() {
$("#list").tabs("#content > div", {effect: 'ajax', history: true});
});​

Ответы [ 2 ]

2 голосов
/ 07 июня 2010

Вот как я бы добавил функциональность клавиатуры в ваш код.

Но я хочу добавить, что после того, как вы посмотрели почти тот же вопрос, который вы разместили за два дня до этого, этот сайт создан для того, чтобы помочь вам найти проблемы в коде, а не напиши все это для тебя. Если вы хотите, чтобы кто-то написал весь код, наймите кого-нибудь, чтобы сделать это, или вам, вероятно, придется научиться делать это самостоятельно (с помощью справки по устранению неполадок от нас по мере необходимости).

В любом случае, я настроил эту демонстрацию , чтобы помочь вам начать. Надеюсь, комментариев будет достаточно, чтобы вы могли понять, что я делал, и вы можете взять это оттуда.

$(function() {
 var list = $('#list'),
     imgPerRow = -1,
     loop = true;
 // find first image y-offset to find the number of images per row
 var topOffset = list.find('img:eq(0)').offset().top,
     numTabs = list.find('a').length - 1,
     current, newCurrent;

 function changeTab(diff){
  current = list.find('a.current').index();
  newCurrent = (loop) ? (current + diff + numTabs + 1) % (numTabs + 1) : current + diff;
  if (loop) {
   if (newCurrent > numTabs) { newCurrent = 0; }
   if (newCurrent < 0) { newCurrent = numTabs; }
  } else {
   if (newCurrent > numTabs) { newCurrent = numTabs; }
   if (newCurrent < 0) { newCurrent = 0; }
  }
  // don't trigger change if tab hasn't changed (for non-looping mode)
  if (current != newCurrent) {
   list.find('a').eq(newCurrent).trigger('click'); // trigger click on tab
  }
 }

 list
  // set up tabs
  .tabs("#content > div", {effect: 'ajax', history: true})

  // find number of images on first row
  .find('img').each(function(i){
   if (imgPerRow < 0 && $(this).offset().top > topOffset ) {
    imgPerRow = i;
   }
  });

  // Set up arrow keys
  // Set to document for demo, probably better to use #list in the final version.
  $(document).bind('keyup', function(e){
   var key = e.which;
   if (key > 36 && key < 41) {
    if (key == 37) { changeTab(-1); }         // Left
    if (key == 38) { changeTab(-imgPerRow); } // Up
    if (key == 39) { changeTab(+1); }         // Right
    if (key == 40) { changeTab(+imgPerRow); } // Down
    e.preventDefault();
   }
  });

 // toggle looping through tabs
 $(':button').click(function(){
  loop = !loop;
  $('#loopStatus').text(loop);
 });

});
0 голосов
/ 07 июня 2010

Вы можете привязать действия к нажатию клавиш, используя

$.keyup(function(e) {
    key = e.which;
    if (key == 37) //code for left arrow key
    {
        ...
    }
});

Быстрый поиск в Google дал мне этот список кодов клавиш: http://www.cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx

...