Javascript-форум (https://javascript.ru/forum/)
-   Элементы интерфейса (https://javascript.ru/forum/dom-window/)
-   -   Выделить интервал элементов (от a до b) двумя кликами (https://javascript.ru/forum/dom-window/61005-vydelit-interval-ehlementov-ot-do-b-dvumya-klikami.html)

Teamur 31.01.2016 10:57

Выделить интервал элементов (от a до b) двумя кликами
 
Выделить интервал=промежуток=цепочк у элементов двумя кликами.
<!doctype html>
<meta charset='utf-8'>
<title>EXAMPLE</title>
<div id='box'>
  <div class='el'>1</div>
  <div class='el'>2</div>
  <div class='el'>3</div>
  <div class='el'>4</div>
  <div class='el'>5</div>
  <div class='el'>6</div>
  <div class='el'>7</div>
  <div class='el'>8</div>
  <div class='el'>9</div>
  <div class='el'>X</div>
</div>
<style>
  body {
    display: flex;
    margin: 0;
    height: 100vh;
    background: #56bddc;
  }
  #box {
    display: flex;
    margin: auto;
    box-shadow: 0 0 5px hsla(0, 0%, 0%, .25);
  }
  .el {
    width: 60px;
    line-height: 60px;
    font-family: consolas;
    font-size: 30px;
    text-align: center;
    cursor: pointer;
    background: #fff;
    -webkit-user-select: none;
    -moz-user-select: none;
  }
  .el:hover { background: #fece9a; }
  .mark { background: #fece9a; }
</style>


Неужели в JavaScript нет такой конструкции:
(from a to b) { ... },
где a - элемент, с которого начинается выделение,
b - элемент, на котором заканчивается выделение.

Итак, :write: :
Например, требуется выделить интервал элементов со 2 по 7 включительно.

Тогда делаем всего два клика:
1) клик по элементу номер 2;
2) клик по элементу номер 7.

Результат: 2,3,4,5,6,7 элементы изменят свой цвет согласно полученному классу .mark ( см. <style> ).

Тогда алгоритм будет такой:
1) клик по элементу a:
-> получить номер элемента в контейнере #box;
-> присвоить элементу класс .a;
-> записать номер в переменную a
2) клик по элементу b:
-> получить номер элемента в контейнере #box;
-> присвоить элементу класс .b
-> записать номер в переменную b
3) присвоить элементам от a до b класс .mark, пробежавшись по ним циклом

Хотелось бы решить задачу, используя последние фишки ES6 и желательно обойтись без массивов.
Цикл for-of, который может перебирать коллекции элементов, а не только массивы:
http://frontender.info/es6-in-depth-...ikollektsiyami

Попытки, что-то сделать:

Получение номера (индекса) элемента в контейнере:
box.onmouseover = function(e) {
  var target = e.target;
  target.onclick = function() {
    target.classList.add('a');
    for (var i = 0; i < box.children.length; i++) {
      if (box.children[i] == target) return console.log(i + 1);
    }
  }
}


Может пригодится:
'use strict'
var i,
      j = i + 1,
      parent = box,
      children = parent.children;
// Вместо children можно использовать элементы от a до b
parent.onmouseover = function() {
  for (i of children) {
    i.classList.add('mark');
  }
}


На Киберфоруме я уже задавал этот вопрос, но ответ получился слишком громоздким. Надеюсь здесь удастся решить иначе.
Если кому будет интересно, результат должен быть таким:
http://www.cyberforum.ru/post8673990.html
Надеюсь, что вы поможете. Спасибо.

destus 31.01.2016 11:48

Не знаю ни ES6, ни цикла for of. Однако можно же просто запоминать ноды старта и энда, а далее используя обход по дереву добавлять соответствующие классы к нужным элементам (включая граничные).

Teamur 31.01.2016 12:11

destus,
можно, но я уже выбился из сил, поэтому и создал тему на этом форуме. Задача вроде не сложная, но сделать не удается. Учусь. Но наверное, я просто перегрузил голову, надо больше отдыхать. Просьба. Комментируйте предлагаемые решения, чтобы все было понятно. Спасибо.

рони 31.01.2016 12:17

Teamur,
вариант без промежуточного выделения , только по клику
<!DOCTYPE HTML>

<html>

<head>
  <title>Untitled</title>
  <meta charset="utf-8">
  <style>
  body {
    display: flex;
    margin: 0;
    height: 100vh;
    background: #56bddc;
  }
  #box {
    display: flex;
    margin: auto;
    box-shadow: 0 0 5px hsla(0, 0%, 0%, .25);
  }
  .el {
    width: 60px;
    line-height: 60px;
    font-family: consolas;
    font-size: 30px;
    text-align: center;
    cursor: pointer;
    background: #fff;
    -webkit-user-select: none;
    -moz-user-select: none;
  }
  .el:hover { background: #fece9a; }
  .mark { background: #fece9a; }
</style>

  <script>
    window.addEventListener("DOMContentLoaded", function() {
    var d = document.querySelectorAll(".el"),
        a = void 0;
    [].forEach.call(d, function(c, b) {
        c.addEventListener("click", function() {
            if (void 0 == a) a = b;
            else {
                var c = Math.min(b, a),
                    e = Math.max(b, a);
                a = void 0;
                [].forEach.call(d, function(a, b) {
                    b >= c && b <= e && a.classList.toggle("mark")
                })
            }
        })
    })
});
  </script>
</head>

<body>
<div id='box'>
  <div class='el'>1</div>
  <div class='el'>2</div>
  <div class='el'>3</div>
  <div class='el'>4</div>
  <div class='el'>5</div>
  <div class='el'>6</div>
  <div class='el'>7</div>
  <div class='el'>8</div>
  <div class='el'>9</div>
  <div class='el'>X</div>
</div>


</body>

</html>

Teamur 31.01.2016 12:21

рони, Спасибо.
Можно ли сделать так, чтобы третий клик, запускал функцию заново = начать новое выделение?

destus 31.01.2016 12:42

Если долго не думать
<div id='box'>
  <div id='e1' class='el'>1</div>
  <div class='el'>2</div>
  <div class='el'>3</div>
  <div class='el'>4</div>
  <div class='el'>5</div>
  <div class='el'>6</div>
  <div class='el'>7</div>
  <div class='el'>8</div>
  <div class='el'>9</div>
  <div class='el'>X</div>
</div>
<style>
  body {
    display: flex;
    margin: 0;
    height: 100vh;
    background: #56bddc;
  }
  #box {
    display: flex;
    margin: auto;
    box-shadow: 0 0 5px hsla(0, 0%, 0%, .25);
  }
  .el {
    width: 60px;
    line-height: 60px;
    font-family: consolas;
    font-size: 30px;
    text-align: center;
    cursor: pointer;
    background: #fff;
    -webkit-user-select: none;
    -moz-user-select: none;
  }
  .el:hover { background: #fece9a; }
  .mark { background: #fece9a; }
</style>

<script>
	
	function positionChild(node)
	{
		var pos = 1;
		for (var x = node.parentNode.firstChild; x != null; x = x.nextSibling, pos++  )
		{
			if ( x.id == node.id && x.nodeType == 1 ) { return pos; break; }	
		}
	}
	
	function addClassName(obj,name)
	{
		obj.className += name; 	
	}
	
	function byPass(startDiv,endDiv)
	{
		for ( var x = startDiv; x != endDiv.nextSibling  ;  x = x.nextSibling )
		{
			addClassName(x,' mark');
		}		
	}
	
	
	var start, end;
	document.getElementById('box').onclick=function fn(e){
		e = e || event;
		var target = e.target;
		if (!start) { start = true; target.id = 'start'; } 
		else
		{
			var startChildPosition, endChildPosition;
			if (!end) 
			{ 
				end = true;
				if (target.id == 'start')
				{
					addClassName(target, ' mark');
				}
				else
				{ 
					target.id = 'end';
				}
			} 
			startDiv = document.getElementById('start');
			endDiv = document.getElementById('end');
			startChildPosition = positionChild(startDiv);
			endChildPosition = positionChild(endDiv);
			if (startChildPosition < endChildPosition) byPass(startDiv,endDiv);
			else byPass(endDiv,startDiv)
		}
	}

</script>

Teamur 31.01.2016 12:45

destus,
Как вариант. Тоже думал насчет nextSibling. )

рони 31.01.2016 13:09

выделение диапазона элементов по двум кликам
 
Цитата:

Сообщение от Teamur
начать новое выделение?

можно
<!DOCTYPE HTML>

<html>

<head>
  <title>Untitled</title>
  <meta charset="utf-8">
  <style>
  body {
    display: flex;
    margin: 0;
    height: 100vh;
    background: #56bddc;
  }
  #box {
    display: flex;
    margin: auto;
    box-shadow: 0 0 5px hsla(0, 0%, 0%, .25);
  }
  .el {
    width: 60px;
    line-height: 60px;
    font-family: consolas;
    font-size: 30px;
    text-align: center;
    cursor: pointer;
    background: #fff;
    -webkit-user-select: none;
    user-select: none;
    -moz-user-select: none;
  }
  .el:hover { background: #fece9a; }
  .mark { background: #fece9a; }</style>

  <script>
  window.addEventListener("DOMContentLoaded", function() {
    function e(a, c) {
        [].forEach.call(b, function(f, d) {
            (d >= a && d <= c || void 0 === a) && f.classList[void 0 === a ? "remove" : "add"]("mark")
        })
    }
    var b = document.querySelectorAll(".el"),
        a = void 0;
    [].forEach.call(b, function(b, c) {
        b.addEventListener("click", function() {
            if (void 0 === a) a = c, e();
            else {
                var b = Math.min(c, a),
                    d = Math.max(c, a);
                a = void 0;
                e(b, d)
            }
        })
    })
});
  </script>
</head>

<body>
<div id='box'>
  <div class='el'>1</div>
  <div class='el'>2</div>
  <div class='el'>3</div>
  <div class='el'>4</div>
  <div class='el'>5</div>
  <div class='el'>6</div>
  <div class='el'>7</div>
  <div class='el'>8</div>
  <div class='el'>9</div>
  <div class='el'>X</div>
</div>


</body>

</html>

Teamur 31.01.2016 13:14

рони, замечательно.
Самое сложное, как я считаю, будет реализовать визуализацию следования выделения за курсором после первого клика по аналогии с выделением текста, когда двигая курсор после зажатия левой кнопки мыши мы ВИДИМ процесс выделения (синяя область). Спасибо

рони 31.01.2016 13:50

Цитата:

Сообщение от Teamur
Самое сложное,

всё решаемо ... +5-7 строк ... попробуйте сами ...


Часовой пояс GMT +3, время: 20:18.