优化 jquery 插件的导航代码

refining navigation code for jquery plugin

我正在开发一个 jquery 插件并尝试改进“下一步”按钮上的代码。该插件的界面如下所示: 最好的办法是先向您展示我的所有代码,然后我会提请您注意我的问题。

HTML:

<html>
<head>
<title>Carousel</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="button_carousel.css">
<script type="text/javascript" charset="utf-8" src="jquery.js"></script>
<script type="text/javascript" carset="utf-8" src="button_carousel.js"></script>

<script>
  $(document).ready(function() {
    //December has 31 days, therefore 31 buttons are required.
    var appointmentDays = [4,7,21];

    function myEventHandler(idvalue) {        
      if($.isFunction(idvalue))return;      
      console.debug("myEventHandler fired...id: " + idvalue);       
    }

    var bc = $('#carousel').button_carousel({
      days_in_month: 31,
      starting_day: 4,
      days_with_appointments: appointmentDays,
      eventHandler: myEventHandler
    });     
});
</script>
</head>
<body>
    <div id="carousel"></div>
</body>
</html>


CSS button_carousel.css:

/* Styling for prev button */
#nav_previous {
padding:0 0 0 0;    
position:absolute;
top:5px;
left:5px;
width:30px;
height: 30px;   
}

#slides {
overflow:hidden;
/* fix ie overflow issue */
position:absolute;
width:300px;
height:33px;
border:1px solid #ccc;
top:5px;
left:47px;
background-color: #F5F6CE;
}

/* remove the list styles, width : item width=85 * total items=31 */    
#slides ul {
position:relative;
left:0px;
top:0px;
list-style:none;
margin:0px;
padding:0px;    
width:2635px;           
}

#slides li {
width:85px;
height:30px;    
float:left;
}

/* Styling for next button */
#nav_next {
padding:0 0 0 0;    
position:absolute;
top:5px;
left:364px;
width:30px;
height: 30px;   
}

#nav_previous a {
display:block; 
width:31px; 
height:32px;
text-indent:-999em; 
outline:0;  
}

#nav_next a {
display:block; 
width:31px; 
height:32px;
text-indent:-999em; 
outline:0;  
}

a#prev {
background:url(images/left_black_arrow.png) no-repeat; 
}

a#prev:hover {
background:url(images/left_white_arrow.png) no-repeat;
}

a#next {
background:url(images/right_black_arrow.png) no-repeat; 
}

a#next:hover {
background:url(images/right_white_arrow.png) no-repeat;
}

input.btn {
 height:30px;
 width:65px;
 padding-right: 5px;
 padding-left: 5px;
 font-weight: bold;
 border:1px solid #000000;
}


JQUERY button_carousel.js:

(function ( $ ) {   

//grab the width and calculate left value
var item_width; 
var left_value;

//create the array of days
var day_names = new Array("SUN","MON","TUE","WED","THU","FRI","SAT");

var btnDefaultBackgroundColor = "#F0F0F0";

var buttonSelected = 0;

var buttonVisible = 0;  
            
$.fn.button_carousel = function(newoptions){
  console.debug("initializing button carousel");
  
  //Set all the properties of the parent div element,
  //no matter what its name
  $(this.selector).css({"background-color": "#D4D6D7",
    "width":"400px",
    "height":"45px",    
    "margin":"0 auto",
    "position":"relative",
    "border":"1px solid #ccc"               
  });     
  
  //Set the defaults
  var defaults = {
    days_in_month: 0,
    starting_day: 0,
    days_with_appointments: null,
    btnAppointmentFontColor: "#FF33EC",
    btnSelectedColor: "#9BE3F7",
    eventHandler: null      
  };          
  
  var options = $.extend({}, defaults, newoptions);
  
  //Bind the eventHandler function to receiving id values from button clicks.
  //See trigger below from all button clicks.
  $(options.eventHandler).bind("CarouselButtonID", function(event,data) {
         //console.debug("bind event id: " + data);
         options.eventHandler(data);
  });
    
  //Create initial carousel interface
  var container = $(this);
  container.append('<div id="nav_previous"><a id="prev" alt="previous button"></a></div><div id="slides"><ul></ul></div><div id="nav_next"><a id="next" alt="next button"></a></div>');
  
  //Set the display property for all div elements
   $(this.selector).each(function(){
       $("div").css("display", "inline-block");
   });
  
  //Use event delegation for the previous and next buttons
  $('div#nav_previous').on('click', 'a#prev', function(event) {
    event.preventDefault();
    //get the right position            
    var left_indent = parseInt($('#slides ul').css('left')) + item_width;
    console.debug("prev button - left_indent: " + left_indent + " item_width: " + item_width);
    //slide the item            
    $('#slides ul').animate({'left' : left_indent}, 200,function(){    

        //move the last item and put it as first item               
        $('#slides li:first').before($('#slides li:last'));           

        //set the default item to correct position
        $('#slides ul').css({'left' : left_value});
    
    });
    //buttonVisible--;  
    //console.debug("buttonVisible: " + buttonVisible);
    //cancel the link behavior            
    return false;
  });
  //Use event delegation for the previous and next buttons
  $('div#nav_next').on('click', 'a#next', function(event) {
    event.preventDefault();
    //get the right position
    //console.debug("next button - calc: " + parseInt($('#slides ul').css('left')));
    var left_indent = 0;
    var calc = parseInt($('#slides ul').css('left'));
    if(calc == 0)
        left_indent = 0;
    else
        left_indent = calc - item_width;
    console.debug("next button - left_indent: " + left_indent + " item_width: " + item_width + " left_value: " + left_value);
    //slide the item
    $('#slides ul').animate({'left' : left_indent}, 200, function () {
        
        //move the first item and put it as last item
        $('#slides li:last').after($('#slides li:first'));                  
        
        //set the default item to correct position
        $('#slides ul').css({'left' : left_value});
    
    });         
    //cancel the link behavior
    return false;
  });

  
  
  //populate unordered list with input type button elements.
  populate_carousel();
  
  item_width = $('#slides li').outerWidth(); 
  left_value = item_width * (-1);
  
  //Use event delegation on the slide buttons.
  $('div#slides ul').on('click', 'li input.btn', function(event) {
    var currentBtnID = $(this).attr('id');
    if(buttonSelected > 0)
    {           
        $( "li input.btn#" + buttonSelected ).css( "background-color", btnDefaultBackgroundColor );
        $(this).css("background-color", options.btnSelectedColor);
        buttonSelected = currentBtnID;          
    }
    else
    {
        $(this).css("background-color", options.btnSelectedColor);
        buttonSelected = currentBtnID;
    }
    $(options.eventHandler).trigger("CarouselButtonID", currentBtnID);      
  });     
  
  
  //Use this public function to reload all new options
  this.reloadAllOptions = function(newoptions){
          
    options = $.extend({}, defaults, newoptions); 
    var container = $('div#slides ul');
    //remove all the buttons
    container.each(function(){
        $( "li" ).remove();     
    });
    //populate unordered list with input type button elements.
    populate_carousel();
    
    var left_indent = parseInt($('#slides ul').css('left')) + item_width;

    //slide the item            
    $('#slides ul').animate({'left' : left_indent}, 200,function(){    

        //move the last item and put it as first item               
        $('#slides li:first').before($('#slides li:last'));           

        //set the default item to correct position
        $('#slides ul').css({'left' : left_value});
    
    });
    
    return this.each(function(){
       console.log(options);
    });
  }
  
  //Use this public function to update and highlight those days that have appointments associated.
  this.setButtonAppointmentFontColor = function(buttonColor){
      if(buttonColor == null)return $(this);
      var newoptions = {
        days_in_month: options.days_in_month,
        starting_day: options.starting_day,
        days_with_appointments: options.days_with_appointments,
        btnAppointmentFontColor: buttonColor,
        btnSelectedColor: options.btnSelectedColor,
        eventHandler: options.eventHandler          
      };
      options = $.extend({}, defaults, newoptions);
      HighlightDays(options.days_with_appointments);
      return this.each(function(){
       console.log(options);
      });
  }
  
  //Use this public function to update and highlight those days that have appointments associated.
  this.setButtonSelectedColor = function(buttonColor){
      if(buttonColor == null)return $(this);
      var newoptions = {
        days_in_month: options.days_in_month,
        starting_day: options.starting_day,
        days_with_appointments: options.days_with_appointments,
        btnAppointmentFontColor: options.btnAppointmentFontColor,
        btnSelectedColor: buttonColor,
        eventHandler: options.eventHandler          
      };
      options = $.extend({}, defaults, newoptions);
      return this.each(function(){
       console.log(options);
      });
  }
  
  //Use this public function to update and highlight those days that have appointments associated.
  this.setAppointmentDays = function(apptDaysArray){
      if(apptDaysArray == null)return $(this);
      var newoptions = {
        days_in_month: options.days_in_month,
        starting_day: options.starting_day,
        days_with_appointments: apptDaysArray,
        btnAppointmentFontColor: options.btnAppointmentFontColor,
        btnSelectedColor: options.btnSelectedColor,
        eventHandler: options.eventHandler          
      };
      options = $.extend({}, defaults, newoptions);
      HighlightDays(apptDaysArray);
      return this.each(function(){
       console.log(options);
      });
  }
  
  //Use this public function to update the days in the month only
  this.setDaysInMonth = function(days, startDay){
      if(days == null)return $(this);
      if(startDay == null)return $(this);
      var newoptions = {
        days_in_month: days,
        starting_day: startDay,
        days_with_appointments: null,
        btnAppointmentFontColor: options.btnAppointmentFontColor,
        btnSelectedColor: options.btnSelectedColor,
        eventHandler: options.eventHandler          
      };
      options = $.extend({}, defaults, newoptions);
      var container = $('div#slides ul');
      //remove all the buttons
      container.each(function(){
        $( "li" ).remove();     
      });
      //populate unordered list with input type button elements.
      populate_carousel();
    
      var left_indent = parseInt($('#slides ul').css('left')) + item_width;

      //slide the item            
      $('#slides ul').animate({'left' : left_indent}, 200,function(){    

        //move the last item and put it as first item               
        $('#slides li:first').before($('#slides li:last'));           

        //set the default item to correct position
        $('#slides ul').css({'left' : left_value});
    
      });
    
      return this.each(function(){
         console.log(options);
      });
  }
  
  this.setEventHandlerFunction = function(someFunction){
      if(someFunction == null)return $(this);
      var newoptions = {
        days_in_month: options.days_in_month,
        starting_day: options.starting_day,
        days_with_appointments: options.days_with_appointments,
        btnAppointmentFontColor: options.btnAppointmentFontColor,
        btnSelectedColor: options.btnSelectedColor,
        eventHandler: someFunction          
      };
      options = $.extend({}, defaults, newoptions);
      
      return this.each(function(){
       console.log(options);
      });
  }
  
  this.setSelected = function(id){
      if(buttonSelected > 0)
      {         
        $( "li input.btn#" + buttonSelected ).css( "background-color", btnDefaultBackgroundColor );
        $("li input.btn#" + id).css("background-color", options.btnSelectedColor);
        buttonSelected = id;            
      }
      else
      {
        $("li input.btn#" + id).css("background-color", options.btnSelectedColor);
        buttonSelected = id;
      }
      //move the desired day of the month to the left side of the carousel
      if(buttonSelected == 1)
          return this;
      
      for(var i = 2; i < options.days_in_month + 1; i++)
      {
        if(i == buttonSelected)
            break;
        //get the right position
        var left_indent = parseInt($('#slides ul').css('left')) - item_width;
        
        //slide the item
        $('#slides ul').animate({'left' : left_indent}, 0, function () {
            
            //move the first item and put it as last item
            $('#slides li:last').after($('#slides li:first'));                  
            
            //set the default item to correct position
            $('#slides ul').css({'left' : left_value});
        
        });
      }
      return this;
  }
  
  this.triggerButtonClicked = function(id){
      $(options.eventHandler).trigger("CarouselButtonID", id);
      return this;
  }
  
  this.isAppointmentDay = function(id){
      if(options.days_with_appointments == null) return false;
      var appday = false;
      var day = 0;
      for(var i = 0; i < options.days_with_appointments.length; i++)
      {
          day = options.days_with_appointments[i];
          if(day == id)
          {
            appday = true;
            break;
          }
      }
      return appday;
  }
  
  function populate_carousel() {
    var index = options.starting_day;
    var container = $('div#slides ul');
      
    for(var i = 1; i < options.days_in_month + 1; i++)
    {
        container.append('<li><input type="button" id="' + i + '" class="btn" value="' + day_names[index] + " " + i + '"></li>');
        if(index < 6)
           index++;
        else
           index = 0; //reset           
    }       
    
    HighlightDays(options.days_with_appointments);
  }

  function HighlightDays(appointmentsArray)
  {
    if(appointmentsArray == null)return;
    //console.debug("HighlightDays(): " + appointmentsArray);
    resetButtonFontColor()
    var identifier;
    var match = false;
    $('li input.btn').each(function(){
        identifier = this.id;
        //console.debug("id: " + identifier);
        match = haveMatch(identifier, appointmentsArray);
        if(match == true)
        {
            $(this).css("color", options.btnAppointmentFontColor);
        }
    });
                
  }

  function haveMatch(number, appointmentsArray){
    var match = false;
    for(var i = 0; i < appointmentsArray.length; i++)
    {
        if(number == appointmentsArray[i])
        {
            match = true;
            break;
        }
    }
    return match;
  }

  function resetButtonFontColor()
  {
    $('li input.btn').each(function(){
      $(this).css("color", '#000000');  //black 
    });
  }
  
  return this.each(function(){
     console.log(options);
  });     
};  

}( jQuery ));

好的...因此,如果您按所示加载所有代码并单击“下一步”按钮(即向右的箭头按钮 -->),您会注意到“日期”按钮前进的方式异常。这些按钮应该一次只前进一个按钮,但是,当您第一次按下 NEXT 按钮时,2 DAY 按钮会前进。那不应该发生。如果您继续按下 NEXT 按钮,DAY 按钮将在之后一次前进一个。问题代码为:

$('div#nav_next').on('click', 'a#next', function(event) {
    event.preventDefault();
    //get the right position
    //console.debug("next button - calc: " + parseInt($('#slides ul').css('left')));
    var left_indent = 0;
    var calc = parseInt($('#slides ul').css('left'));
    if(calc == 0)
        left_indent = 0;
    else
        left_indent = calc - item_width;
    console.debug("next button - left_indent: " + left_indent + " item_width: " + item_width + " left_value: " + left_value);
    //slide the item
    $('#slides ul').animate({'left' : left_indent}, 200, function () {
        
        //move the first item and put it as last item
        $('#slides li:last').after($('#slides li:first'));                  
        
        //set the default item to correct position
        $('#slides ul').css({'left' : left_value});
    
    });
    //cancel the link behavior
    return false;
  });

箭头按钮只是指向 css 中定义的箭头图像的锚标记,您可能需要使用它来获得可点击的内容。

我已经玩这个东西好几个小时了,我不明白为什么按钮在第一次点击时会提前两天按钮。我需要另一双眼睛来解决这个问题,因为我的眼睛正在上釉。请指教

我对你的代码做了一些修改:

 //Use event delegation for the previous and next buttons
    $('div#nav_next').on('click', 'a#next', function(event) {
      event.preventDefault();
      //get the right position
      //console.debug("next button - calc: " + parseInt($('#slides ul').css('left')));
      var left_indent = 0;
      var calc = parseInt($('#slides ul').css('left'));
      console.log(calc);
      //  if (calc == 0)
      //    left_indent = 0;
      //  else
      //  left_indent = calc - item_width;
      console.debug("next button - left_indent: " + left_indent + " item_width: " + item_width + " left_value: " + left_value);
      //slide the item
      $('#slides ul').animate({
        'left': '-' + item_width
      }, 200, function() {

        //move the first item and put it as last item
        $('#slides li:last').after($('#slides li:first'));

        //set the default item to correct position
        $('#slides ul').css({
          'left': 0
        });

      });
      //cancel the link behavior
      return false;
    });

动画完成后,将 "left" 值设置为 item_width (-85px),同时将元素移动到其父元素的末尾。这导致 -85px 的左边距没有元素来填充它(我们移动到元素的末尾)因此第二个元素将填充 space。

此外,一个有效的 JSFiddle(减去图像):https://jsfiddle.net/0x402dte/