jQuery 选项卡的键盘导航

发布于 2024-09-04 16:11:00 字数 11081 浏览 5 评论 0原文

如何为具有历史记录的 jQuery 选项卡制作键盘导航左/上/右/下(如照片库)功能? http://dl.dropbox.com/u/6594481 中没有键盘功能的演示/tabs/index.html

所需功能:

  1. 从第一层到最后一层的嵌套 ajax 选项卡
  2. 在键盘上 top/down 进行选择并 CSS 显示active键盘上 左/右更改后/前活动嵌套ajax选项卡的内容选项卡是
  3. 一个额外选项,使活动嵌套ajax具体嵌套ajax选项卡级别上“cursor-on”上的选项卡

阅读更多详细问题以及示例图片 https://stackoverflow.com/questions/2975003/jquery-tools-to-make-keyboard-and-cookies-feature-for-ajaxed-tabs-with -历史

/**
 * @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});
});​

How to make Keyboard navigation left/up/right/down (like for photo gallery) feature for jQuery Tabs with history? Demo without keyboard feature in http://dl.dropbox.com/u/6594481/tabs/index.html

Needed functions:

  1. on keyboard top/down make select and CSS showactivenested ajax tabs from 1-st to last level
  2. on keyboard left/right changeback/forwardcontent ofactivenested ajax tabs tab
  3. an extra option, make active nested ajax tab on 'cursor-on' on concrete nested ajax tabs level

Read more detailed question with example pictures in 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});
});​

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

十秒萌定你 2024-09-11 16:11:00

以下是我将键盘功能添加到您的代码中的方法。

但是,我想补充一点,在查看几乎相同的 您在此问题前两天发布的问题,该网站的建立是为了帮助您发现代码中的问题,而不是为您编写所有代码。如果您希望有人编写所有代码,请雇用某人来完成,否则您可能必须学习如何自己完成(根据需要需要我们提供故障排除帮助)。

不管怎样,我设置了这个演示来帮助您入门。希望有足够的评论让您能够理解我在做什么,并且可以从那里开始。

$(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);
 });

});

Here is how I would add the keyboard functionality to your code.

But, I want to add that after looking at pretty much the same question you posted two days prior to this one, that this site is set up to help you find problems in your code, not to write it all for you. If you want someone to write all the code, hire someone to do it or you'll probably have to learn how to do it yourself (with troubleshooting help from us as needed).

Anyway, I set up this demo to help you get started. Hopefully there are enough comments that you can understand what I was doing and you can take it from there.

$(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);
 });

});
冷弦 2024-09-11 16:11:00

您可以使用以下方法将操作绑定到按键:

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

快速谷歌搜索为我提供了以下关键代码列表:http://www.cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx

You can bind actions to key presses by using

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

A quick google search got me this list of key codes: http://www.cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文