уважаемые знатоки и профессионалы! Из предложенных мне схожих тем по моим запросам на StackOverflow я ничего не нашёл. Возможно, плохо искал или неправильно определял заголовок проблемы. Как бы там не было, вопрос в следующем: Есть самописный слайдер на HTML/CSS/JS (как понятно из заголовка), написанный новичком, то есть мной. Если над слайдером быстро поводить мышкой, то вместо обычной смены слайдов происходит их быстрая прокрутка («пролёт»). В коде вроде бы ошибок нет (иначе бы не работало), но как избавиться от непонятного бага не знаю. Прошу Вашей помощи. Код прилагаю ниже. С уважением, Dmitry
P.S. Вот ссылка на CodePen, - где всё можно проверить в действии. Из-за текста на слайдах не обессудьте;)
<body>
<div class="container">
<div class="slider" onmouseenter="handler(event); stop()" onmouseleave="handler(event); start()">
<div class="slide slide-1 animate"> <!--onmouseenter="stop()" onmouseleave="start()"-->
<p class="title">HTML5 и CSS3</p>
<p>Вёрстка:</p>
<ul>
<li>страниц сайтов по PSD и XCF-макетам;</li>
<li>вертикальных и горизонтальных меню;</li>
<li>форм обратной связи;</li>
<li>слайдеров на подобие этого.</li>
</ul>
</div>
<div class="slide slide-2 animate"> <!--onmouseenter="stop()" onmouseleave="start()"-->
<p class="title">5-6 дней</p>
<p>Среднее время вёрстки двухстраничного сайта.</p>
</div>
<div class="slide slide-3 animate"> <!--onmouseenter="stop()" onmouseleave="start()"-->
<a href="#" class="btn btn-blue">Успейте сделать заказ</a>
</div>
<div class="slider-toggles"> <!--onmouseenter="stop()" onmouseleave="start()"-->
<a href="#slide-1" class="toggle toggle-1" onclick="setSlide(0, 'gray', 'white')"></a>
<a href="#slide-2" class="toggle toggle-2" onclick="setSlide(1, 'gray', 'white')"></a>
<a href="#slide-3" class="toggle toggle-3" onclick="setSlide(2, 'gray', 'white')"></a>
</div>
</div>
</div>
</body>
body {
padding: 0px;
margin: 0px;
font-size: 20px;
font-family: "Arial", sans-serif;
line-height: 24px;
background-color: #D7CCC8;
}
.container {
position: relative;
width: 640px;
min-height: 380px;
padding: 0px 5px;
margin: 0 auto;
/* outline: 2px solid gray; */
background-color: #BCAAA4;
}
.slider-toggles {
position: relative;
top: 345px;
width: 85px;
height: 25px;
/* outline: 2px solid gray; */
margin: 0 auto;
}
.slider-toggles .toggle {
position: absolute;
display: inline-block;
width: 5px;
height: 5px;
border: 10px solid #0088cc;
border-radius: 50%;
background-color: #0088cc;
}
.slider-toggles .toggle-1 {
left: 0px;
}
.slider-toggles .toggle-2 {
left: 30px;
}
.slider-toggles .toggle-3 {
left: 60px;
}
.slider-toggles .toggle:focus {
background-color: white;
}
.slider .slide {
position: absolute;
/*z-index: 1;*/
top: 5px;
left: 8px;
width: 575px;
height: 370px;
padding: 0px 30px;
font-weight: bold;
color: white;
background-color: #8D6E63;
opacity: 0;
}
.slider .slide p.title {
margin-top: 50px;
font-size: 24px;
}
.slider .slide ul {
padding: 0px;
padding-left: 20px;
margin: 0px;
}
.slider .slide ul > li {
font-size: 18px;
}
.slider .slide-1 {
/*z-index: 2;*/
opacity: 1;
}
.slider .slide-1:after {
content: "";
position: absolute;
top: 20px;
right: 15px;
width: 200px;
height: 290px;
background: url("https://images.unsplash.com/photo-1556772122-5759ebfd70c1?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ") no-repeat 0 0;
background-size: 200px auto;
}
.slider .slide-2 p.title {
margin-bottom: 5px;
}
.slider .slide-2 p {
margin-top: 10px;
font-size: 20px;
}
.slider .slide-2:after {
content: "";
position: absolute;
top: 125px;
left: 175px;
width: 280px;
height: 200px;
background: url("https://images.unsplash.com/photo-1461632830798-3adb3034e4c8?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ") no-repeat 0 0;
background-size: 280px auto;
}
.btn {
display: block;
padding: 15px 10px;
padding-bottom: 20px;
margin-top: 255px;
border: 2px solid gray;
color: white;
text-align: center;
text-decoration: none;
text-transform: uppercase;
background-color: gray;
}
.btn-blue {
border-color: #4A148C;
background-color: #4A148C;
}
.slider .slide-3 .btn:active {
color: rgba(255, 255, 255, 0.3);
}
.slider .slide-3:after {
content: "";
position: absolute;
top: 20px;
left: 150px;
width: 340px;
height: 220px;
background: url("https://images.unsplash.com/photo-1490724500206-cd5482e02b9e?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ") no-repeat 0 0;
background-size: 340px auto;
}
.animate {
transition: opacity 2750ms ease-in;
}
//Теперь знаю, что проще было использовать try-catch-finally
function ErrorClass(){
//EXIT_OUT_LIMITS_OF_ARRAY
this.ERROR_SLIDE_1 = function(){
return 100;
}
this.ERROR_TOGGLES_1 = function(){
return 101;
}
this.errorCode = function(error_code){
switch(error_code){
case this.ERROR_SLIDE_1(): return 'ERROR_EXIT_OUT_LIMITS_OF_ARRAY_CLASS_SLIDE';
case this.ERROR_TOGGLES_1(): return 'ERROR_EXIT_OUT_LIMITS_OF_ARRAY_CLASS_TOGGLES';
default: return 'UNKNOWN_ERROR';
}
}
}
//Работа со слайдами
function Slide(){
var pointer = 0;
var slides = document.getElementsByClassName("slide");
var errorClass = new ErrorClass();
this._checkPointer = function(number){
if((number >= 0) && (number < slides.length)) {
return true;
} else {
return false;
}
}
this.setPointer = function(number){
if(this._checkPointer(number)){
pointer = number;
}
}
this.getPointer = function(){
return pointer;
}
this.getCurrentSlide = function(){
return slides[pointer];
}
this.getSlidesLength = function(){
return slides.length;
}
this.getSlide = function(number){
if(this._checkPointer(number)){
return slides[number];
} else {
return errorClass.ERROR_SLIDE_1();
}
}
this.nextSlide = function(){
return slides[pointer++];
}
this.prevSlide = function(){
return slides[pointer--];
}
this.startNew = function(){
this.setPointer(0);
}
}
//Работа с переключателями
function Toggles(){
var toggles = document.getElementsByClassName('toggle');
var errorClass = new ErrorClass();
var SUCCESSFUL = 0;
this.getToggles = function(){
return toggles;
}
this._setCurrentToggle = function(pointer, bgCurrent){
if((pointer < 0) || (pointer >= this.getToggles().length)){
return errorClass.ERROR_TOGGLES_1();
} else {
toggles[pointer].style.backgroundColor = bgCurrent;
return SUCCESSFUL;
}
}
this._clearToggles = function(bgNoActive){
var i = 0;
for(i = 0; i < this.getToggles().length; i++){
toggles[i].style.backgroundColor = bgNoActive;
}
}
this.activateToggle = function(pointer, bgNoActive, bgCurrent){
var error;
this._clearToggles(bgNoActive);
error = this._setCurrentToggle(pointer, bgCurrent);
return error;
}
}
//status of flag: 0 - слайдер запущен; 1 - остановлен
function FlagStop(){
var flag = 0;
this.setFlag = function(status){
if((status == 0) || (status == 1)){
flag = status;
}
}
this.getFlag = function(){
return flag;
}
}
var slide = new Slide();
var toggles = new Toggles();
var flagStop = new FlagStop();
var errorClass = new ErrorClass();
//Установка в качестве текущего необходимого слайда
function setSlide(number, bgNoActive, bgCurrent){
var i = 0;
var error_code = 0;
for(i = 0; i < slide.getSlidesLength(); i++){
error_code = slide.getSlide(i);
if(error_code != errorClass.ERROR_SLIDE_1()){
error_code.style.opacity = "0";
} else {
return error_code;
}
}
error_code = slide.getSlide(number)
if(error_code != errorClass.ERROR_SLIDE_1()){
error_code.style.opacity = "1";
} else {
return error_code;
}
slide.setPointer(number);
toggles.activateToggle(number, bgNoActive, bgCurrent);
return 0;
}
//функция прокрутки
function play() {
var timerID = 0;
var err = 0;
if(flagStop.getFlag() == 1){
clearTimeout(timerID);
return;
}
if(slide.getPointer() < slide.getSlidesLength()){
err = setSlide(slide.getPointer(), '#0088cc', 'white');
if(err) {
this.alert(errorClass.errorCode(err));
}
slide.nextSlide();
} else {
slide.startNew();
}
if(flagStop.getFlag() == 0){
timerID = setTimeout(play, 8000);
}
}
function stop(){
flagStop.setFlag(1);
}
function start(){
var timerID = 0;
flagStop.setFlag(0);
timerID = setTimeout(play, 16000);
}
//в попытках понять/определить проблему
function handler(event){
console.log(event.type + '[target: ' + event.target.className + '; relatedTarget: ' + event.relatedTarget.className +']\n');
}
window.onload = play();
Всё, что я напишу в данном ответе - предположения, но то, что всё заработало - это факт!
1. Понадобится глобальный timerID. Можно написать класс для работы с ним на javascript в функциональном стиле, чтобы его нельзя было так просто изменить, только через интерфейс.
2. Уберём из функции play() возможность остановки рекурсии и сам рекурсивный вызов. Получим:
function play() {
var err = 0;
if(slide.getPointer() < slide.getSlidesLength()){
err = setSlide(slide.getPointer(), '#0088cc', 'white');
if(err) {
this.alert(errorClass.errorCode(err));
}
slide.nextSlide();
} else {
slide.startNew();
}
}
function stop(){
clearInterval(timerID);
}
function start(){
timerID = setInterval(play, 11000);
}
window.onload = start();
С уважением
P.S. По крайней, мере так намного лучше
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
В общем передаю через Ajax некоторые параметры и вижу такую картину:
Хотите улучшить этот вопрос? Обновите вопрос так, чтобы он вписывался в тематику Stack Overflow на русском
Делаю небольшое SPA с несколькими страницами, навигацию выполняю средствами react-routerПри клике на <Link /> страницы Route рендерит нужный компонент...