Вот рабочий пример всего ниже .
Мой подход заключается в том, чтобы сначала упростить HTML:
<div id="slide_nav">
<a href="#" class="prev">« Previous</a>
<a href="#" class="next">Next »</a>
</div>
<ul id="slide_container">
<li class="one slide">one</li>
<li class="two slide focused">two</li>
<li class="three slide">three</li>
</ul>
Вместо использования всех фиксированных значений ширины (например, width: 9999px;
) я бы установил для всех атрибутов li ширину 100% и использовал бы абсолютное положение. Тогда вы можете просто изменить атрибут left
, чтобы перемещать вещи.
#slide_container li {
width: 100%;
height: 600px;
display: block;
position: absolute;
top: 0;
left: 0;
}
#slide_container .one {
background-color: #900;
left: -100%;
}
#slide_container .two {
display: block;
background-color: #090;
}
#slide_container .three {
background-color: #009;
left: 100%;
}
Поначалу это может показаться сложным, поскольку animate
не очень сильно любит проценты. Но ваши элементы будут автоматически заполнять свой контейнер, поэтому стоит выполнить это небольшое преобразование при загрузке страницы, чтобы все это заработало:
// change all percentages to pixels because animate hates percent
// and seems to lose position over time
var base = 0-$('#slide_container li').eq(0).outerWidth(true);
$('#slide_container li').each(function() {
var $this = $(this);
$this.css('left', base+'px');
base += $this.outerWidth(true);
});
Затем создайте плагин, который упростит структуру вашего кода:
$.fn.extend({
slideMyDivs: function(method, amount) {
if (!amount) {
amount = this.eq(0).outerWidth(true);
}
switch (method) {
case 'prev':
this.stop(true, true).animate({
left: '+=' + amount
}, {
duration: 600,
easing: 'easeInOutExpo'
});
break;
case 'next':
this.stop(true, true).animate({
left: '-=' + amount
}, {
duration: 600,
easing: 'easeInOutExpo'
});
break;
default:
throw new Error('Invalid method: ' + method);
}
return this;
}
});
Тогда я бы настроил метод отключения ссылок при достижении конца / начала списка:
function toggleLinks() {
if (!hasPrev()) {
$('#slide_nav .prev')
.addClass('disabled')
.bind('click.disabled', function() { return false; });
}
else {
$('#slide_nav .prev').removeClass('disabled').unbind('.disabled');
}
if (!hasNext()) {
$('#slide_nav .next').addClass('disabled').bind('click.disabled', function() {
return false;
});
}
else {
$('#slide_nav .next').removeClass('disabled').unbind('.disabled');
}
}
В качестве альтернативы, если вы хотите, чтобы он возвращался назад (что немного выходит за рамки этого вопроса), вы можете переместить последний элемент в начало, установив для него атрибут left
css примерно так:
function send_to_start($elm) {
var newPos = $('#slide_container li').eq(0).pos().left - $elm.outerWidth(true);
$elm.css('left', newPos+'px');
}
Тогда я бы настроил события так:
$('#slide_nav a').mouseenter(function() {
if ($(this).hasClass('disabled')) {
return false;
}
else {
var p = $(this).hasClass('prev') ? 'prev' : 'next';
showPreview(p);
}
}).mouseleave(function() {
cancelPreview($(this).hasClass('prev') ? 'prev' : 'next');
});
$('#slide_nav .prev').click(function() {
cancelPreview('prev');
if (hasPrev()) {
$('#slide_container li').slideMyDivs('prev').filter('.focused').removeClass('focused').prev().addClass('focused');
toggleLinks();
}
return false;
});
$('#slide_nav .next').click(function() {
cancelPreview('next');
if (hasNext()) {
$('#slide_container li').slideMyDivs('next').filter('.focused').removeClass('focused').next().addClass('focused');
toggleLinks();
}
return false;
});
А вот функции misc, вызываемые этими обработчиками событий:
function showPreview(method) {
var p = method == 'prev',
$n = p ? $('#slide_container li.focused').prev() : $('#slide_container li.focused').next();
$n.addClass('previewActive')
.slideMyDivs((p ? 'prev' : 'next'), '75');
}
function cancelPreview(method) {
var p = method == 'prev';
$('#slide_container li.previewActive')
.removeClass('previewActive')
.slideMyDivs((p ? 'next' : 'prev'), '75');
}
function hasPrev() {
return $('#slide_container .focused').prev().length > 0;
}
function hasNext() {
return $('#slide_container .focused').next().length > 0;
}
function toggleLinks() {
if (!hasPrev()) {
$('#slide_nav .prev')
.addClass('disabled')
.bind('click.disabled', function() { return false; });
}
else {
$('#slide_nav .prev').removeClass('disabled').unbind('.disabled');
}
if (!hasNext()) {
$('#slide_nav .next').addClass('disabled').bind('click.disabled', function() {
return false;
});
}
else {
$('#slide_nav .next').removeClass('disabled').unbind('.disabled');
}
}