JSFiddle - React, Tailwind, and code Playground

by Qwerty_Wasd

HTML

<div class="workSpace">
	<div class="box" id="boxOne">1</div>
	<div class="box" id="boxTwo">2</div>

	<div class="buttons">
		<div class="stopButton" id="stopOne">"Остановить 1" НЕ работает</div>
		<div class="stopButton" id="stopTwo">"Остановить 2" НЕ работает</div>
		<div class="stopButton" id="stopAll">"Остановить все" работает</div>
	</div>
</div>

CSS

html, body{
	margin: 0px 0px 0px 0px;
	width:  100%;
	height:  100%;
	overflow:  hidden;
}

.workSpace{
	width: 100%;
	height: 100%;
	background: #eee5ff;
	font-family: comic sans ms;
	overflow:  hidden;
}

.buttons{
	position:  relative;
	top: 450px;
}

.stopButton{
	margin-left: 10px;
	padding: 10px 10px 10px 10px;
	cursor: pointer;
	letter-spacing: 1px;
	border: 2px solid #eee5ff;
	border-radius: 7px;
	color:  #fff;
	display: inline-block;
	text-align: center;
}
.stopButton:hover{
	/* background: #eee5ff; */
	border: 2px solid #000000;
}

.box{
	text-align:  center;
	color: #fff;
}

#boxOne{
	width: 50px;
	height: 50px;
	font-size: 33px;
	overflow: hidden;
	position: absolute;
	background: #795548;
	left: 180px;
	top: 20px;
}

#boxTwo{
	width: 200px;
	height: 100px;
	font-size: 67px;
	overflow:  hidden;
	position: absolute;
	background: #009688;
	left: 180px;
	top: 90px;
}

#boxThree{
	width: 130px;
	height: 61px;
	font-size: 25px;
	overflow:  hidden;
	position: absolute;
	background: #dad2e9;
	color: #eee5ff;
	text-align:  center;
	line-height: 25px;
	left: 180px;
	top: 210px;
}

#stopOne{
	width: 140px;
	background: #795548;
}

#stopTwo{
	width: 140px;
	background: #009688;
}

#stopAll{
	width: 150px;
	background: linear-gradient(to top left, #009688, #795548);
}

#stopOne:active{
	background: #00af00;
}
#stopTwo:active{
	background: #00af00;
}
#stopAll:active{
	background: #00af00;
}

JavaScript

// В скором времени var станет deprecated. ES2015 подарил нам let и const
let box = document.querySelectorAll('.box'),
		workSpace = document.querySelectorAll('.workSpace')[0],
		workArea = {},  coord = {}, interval = {};

//Когда не нужно возвращать массив, старайтесь не использовать без конечной ссылки map\filter\reduce и т.д. Неименованный список будет не доступ сборщику мусора. И у  коллекций тоже есть метод forEach
box.forEach((e,i)=>{
	workArea[i] = {
		x : workSpace.offsetWidth - e.offsetWidth,
		y : workSpace.offsetHeight - e.offsetHeight
	};
	coord[i] = {
		x: 0,
		y: 0
	};
	moveRight(e,i);
});

function moveRight(e,i){
	interval[i] = requestAnimationFrame(  ()=>moveRight(e,i)  );
	if(coord[i].x <= workArea[i].x){
		coord[i].x += 5;
		e.style.left = coord[i].x + 'px';
	}
	if(coord[i].x > workArea[i].x){
		cancelAnimationFrame(interval[i]);
	}
}

// остановить по-отдельности
//Если делегирование все-таки не используете, назначайте обработчик в мапе, а не в цикле
// И старайтесь там где возможно использовать живые коллекции,
//во-превых они быстрее отрабатывают;
//во-вторых если у вас смениться дерево элемента, такая коллекция подхватит изменения.
Array.from(document.getElementsByClassName('stopButton'),(e,i)=>{
	e.addEventListener('click', ()=> {
		cancelAnimationFrame(interval[i]);
	})});


// остановить все
//Ну и напоследок, .onclick это свойство, в которое запишется всего одно событие
//или перезапишет, если уже было. Только addEventListener пополняет очередь.
//Лучше привыкать писать методом.
// И в обработчик лучше пихать именованную функцию или функциональное выражение.
// По этой ссылке можно будет удалить обработчик при необходимости.
// Например помочь сборщику перед выходом со страницы.
//Но для этого приложения это не критично так что....
document.querySelector('#stopAll').addEventListener('click', ()=> {
	for(i=0; i<Object.keys(interval).length; i++){
		cancelAnimationFrame(interval[i]);
	}
});