Выделить интервал элементов (от 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 Надеюсь, что вы поможете. Спасибо. |
Не знаю ни ES6, ни цикла for of. Однако можно же просто запоминать ноды старта и энда, а далее используя обход по дереву добавлять соответствующие классы к нужным элементам (включая граничные).
|
destus,
можно, но я уже выбился из сил, поэтому и создал тему на этом форуме. Задача вроде не сложная, но сделать не удается. Учусь. Но наверное, я просто перегрузил голову, надо больше отдыхать. Просьба. Комментируйте предлагаемые решения, чтобы все было понятно. Спасибо. |
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> |
рони, Спасибо.
Можно ли сделать так, чтобы третий клик, запускал функцию заново = начать новое выделение? |
Если долго не думать
<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> |
destus,
Как вариант. Тоже думал насчет nextSibling. ) |
выделение диапазона элементов по двум кликам
Цитата:
<!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> |
рони, замечательно.
Самое сложное, как я считаю, будет реализовать визуализацию следования выделения за курсором после первого клика по аналогии с выделением текста, когда двигая курсор после зажатия левой кнопки мыши мы ВИДИМ процесс выделения (синяя область). Спасибо |
Цитата:
|
Цитата:
|
destus,
На данном этапе ie обходим стороной. EDGE - не имею ничего против ) |
рони,
5-7 строк. Увы не смогу осилить. math.min() вижу первый раз. Плюс ко всему, я вообще поразился тому, как вам удается так сокращать код. Уменя работают только простейшие скрипты, такие как добавить/удалить класс; добавить обработчики, изменить стили, вывести алерт в конце концов :lol: А при попытке усложнить, добавить функционал - все катится к чертям. А когда есть хоть какой-то работающий прототип, появляется и мотивация. На данный момент я изучаю совр. учебник JavaScript Кантора. Поэтому прошу вас, не только предложить решение, но и объяснить принцип действия. Вот здесь выделение следует за курсором после клика, но код уж больно громоздкий. https://jsfiddle.net/e5cbuoxf/7/ |
Цитата:
<!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; } .blue, .blue:hover{ background: #0000FF; } </style> <script> window.addEventListener("DOMContentLoaded", function() { function d(b, c, d) { [].forEach.call(a, function(e, a) { (a >= c && a <= d || void 0 === c) && e.classList[void 0 === c ? "remove" : "add"](b) }) } var a = document.querySelectorAll(".el"), b = void 0; [].forEach.call(a, function(a, c) { a.addEventListener("mouseover", function() { if (void 0 !== b) { var a = Math.min(c, b), e = Math.max(c, b); d("blue"); d("blue", a, e) } }); a.addEventListener("click", function() { if (void 0 === b) b = c, d("mark"), d("blue", b, b); else { var a = Math.min(c, b), e = Math.max(c, b); b = void 0; d("blue"); d("mark", a, e) } }) }) }); </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> |
Если хочется ES6, то
как говорит рони, для медитации :D Symbol.iterator и nextElementSibling. |
Lemme, Но позволит ли это сократить код? )
Блин, мне больше ничего не надо. Давайте добьем эту тему. Конечно кто я такой, всего лишь Teamur. Думаю тема вполне интересна - ВЫДЕЛЕНИЕ ДИАПАЗОНА (не в обиду другим темам). Медитация тоже не помешает. Наше дело правое и тд, за Родину! Я просто минималист и перфекционист. Так уж вышло. Поэтому не успокоюсь пока не получу компактное и быстрое! Да сейчас я мало что умею делать на JavaScript. Но не могу бросить. Надо сделать! Если что-то смогу сочинить я здесь выложу. Буду держать тему на плаву. Какие еще варианты будут? ))))))) |
рони,
я благодарен вам, за внимание к теме. Спасибо. замечательное решение. Есть ли еще варианты. Хочу увидеть максимальное количество решений. Даже если посмотреть все мои темы можно заметить, что все они сводятся к одному - выделение интервала. Хоть я пока слаб в Javascript, я стремлюсь максимально четко сформулировать задачу, выкладываю фрагменты кода, которые можно использовать, но со сборкой этих фрагментов в единое целое у меня проблема. Спасибо |
Teamur,
сейчас код 30 строк можно сократить строк на 10 ... но всё уже пас ... и лучше наоборот добавить эти 10 с недостающим сейчас функционалом. |
рони,
Если кликнуть первый раз, отвести курсор и не нажимать второй раз, а затем спустить курсор за контейнер #box, мы увидим синюю область. Как сделать так, чтобы при покидании #box, синим остался только тот элемент, на котором был совершен первый клик. А при возвращении курсора, все работало бы как до покидания контейнера = выделение двигается за курсором. |
Цитата:
Цитата:
|
Наверное так :yes: .
box.onmouseout = function() { el.classList.remove('blue'); a.classList.add('blue'); } |
Код легко оптимизируется, однако лень.
<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; } .mark { background: #fece9a; } .blue { background: blue; } </style> <script> var CSSClass = {}; CSSClass.is = function(e,c){ var classes = e.className; return e.className.search("\\b" + c +"\\b") != -1; } CSSClass.add = function(e,c) { if (CSSClass.is(e,c)) return; if (e.className) c = ' ' + c; e.className += c; } CSSClass.remove = function(e,c) { e.className = e.className.replace(new RegExp("\\b" + c+"\\b\\s*", "g"), ''); } 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 byPass(startDiv,endDiv) { for ( var x = startDiv; x != endDiv.nextSibling ; x = x.nextSibling ) { CSSClass.add(x,'mark'); CSSClass.remove(x,'blue'); } } function init() { var div = document.getElementById('box'); for (var x = div.firstChild; x != null; x = x.nextSibling) { x.className = 'el'; x.id = ''; } } function stopped() { start = false; end = true; } var start, end; var startDiv, endDiv; document.getElementById('box').onclick = function(e){ var e = e || event; var target = e.target; if (!start) { init(); start = true; end = false; target.id = 'start'; CSSClass.add(target,'mark'); startDiv = document.getElementById('start'); } else { var startChildPosition, endChildPosition; if (!end) { end = true; if (target.id == 'start') { CSSClass.add(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); } } stopped(); } } document.getElementById('box').onmouseover = function(e){ var e = e || event; var target = e.target; if (start) { if (target.id != 'start') { target.id = 'currNode'; if ( positionChild(target) > positionChild(startDiv) ) { for (var x = startDiv.nextSibling; x != target.nextSibling; x = x.nextSibling) { CSSClass.add(x,'blue'); } } else { for (var x = startDiv.previousSibling; x != target.previousSibling; x = x.previousSibling) { CSSClass.add(x,'blue'); } } target.id = ''; } } } document.getElementById('box').onmouseout = function(e){ var e = e || event; var target = e.target; if (start && !end) { for (var x = target.parentNode.firstChild; x != null; x = x.nextSibling) { if (x.id != 'start') CSSClass.remove(x,'blue'); } if (target.id != 'start') { if( CSSClass.is(target,'blue')) { CSSClass.remove(target,'blue'); } } } } </script> |
destus,
Спасибо за усилия. Спасибо за гиперскрипт. Пойду-ка пройдусь, надо положить за интернет...)))) |
При выведении курсора за контейнер, вся синева исчезает кроме того элемента по которому кликнули первым, то есть, если нужно выделить интервал от 2 до 7, это значит что если кликнуть по 2-му элементу - он станет синим. Потом если сдвинуть курсор до элемента 7, находясь в контейнере, а затем спустить курсор вниз за пределы контейнера, синька должна исчезнуть со всего диапазона, кроме элемента 2, так как он был кликнут первым. Вот такая расшифровка у меня... )))))
|
Цитата:
|
destus,
Во, то что надо! Вы всё правильно поняли. Молодец! ) Осталось одно лишь - сократить! Будем разбираться как работает. |
Цитата:
|
Господа, давайте элементу a (первый кликнутый) присвоим например красный цвет, а конечному элементу b - зеленый. А сам интервал будет синим. К тому же в стилях можно будет задать любые цвета по желанию. Можно взять цвета помягче, чтобы не резало глаза. Начальный элемент выделения (a) - pink, конечный (b) - lightgreen, а промежуток - #ccc, например. Промежуток обзовем range.
Вешаем на детей контейнера box обработчики клика: var el = document.querySelectorAll('.el'); for (var i = 0; i < el.length; i++) { el[i].addEventListener('click', selectRange); } selectRange() { var a, b, range; 1) Получить номер элемента a и записать его в переменную 2) Добавить обработчик наведения на элементы, а затем сразу же динамически получать номер элемента при наведении, записывать его в переменную b. 3) пройтись циклом from a to b } Вот что я знаю и могу на Javascript. Всё, ребята. Поэтому и сижу, жду ответа, как сказал рони. Надеюсь что все закончится хорошо. )))) |
Teamur,
даёшь градиент!!!:lol: |
:write: :lol: Эх была ни была, градиенты, тени, скругления.
А если серьезно, обойдемся и без них. Ведь простое всегда можно усложнить, а вот наоборот - невсегда... |
Может сделать своеобразный счетчик кликов мышью?
А получение номера элемента выполнять ПРЕДВАРИТЕЛЬНО, еще ДО кликов. Первый клик - присваиваем переменной a номер, полученный предварительно при наведении. Далее следуя за курсором, подсвечиваем элементы от a до текущего элемента, над которым находится курсор, постоянно обновляя переменную b Слова словами, но я уверен, что оптимальное решение задачи займет строк 15-20. А здесь нечто похожее, но реализованное на CSS: https://jsfiddle.net/hma21mxm/ Но хотелось бы обойтись без input-ов, label-ов. Только div-ы. Все, что я могу на данный момент - предлагать хоть что-то, имеющее отношение к теме. |
Teamur,
https://jsfiddle.net/daniilKhanin/r48q2o6e/1/ да уж... когда начинал писать не думал что такой ГО...код получится))))) |
DynkanMaclaud,
сложно вернуть синий фон за курсором, осообенно если блоки в 2 ряда. |
Цитата:
Если я правильно понял автора, то это когда нажимаем любой элемент, растягиваем на n элементов вправо(влево), а затем убираем курсор за пределы контейнера, то синяя область исчезает. |
destus,
Верно. DynkanMaclaud, синяя область должна следовать за курсором пока вы не совершите второй клик, но в пределах контейнера. Другими словами, если увести курсор со второго ряда на первый, то синька со второго ряда пропадет, а будет уже на первом ряду у курсора, естественно при этом цепь синих элементов не должна нигде прерываться. |
рони,
Teamur, исправил https://jsfiddle.net/daniilKhanin/r48q2o6e/3/ |
Эххх вот если бы рони,
показал бы свой пример, где я уверен код был бы не таким ужасным... |
Цитата:
Цитата:
|
DynkanMaclaud, спасибо
Практически как надо. Осталось это: Первый клик -> Отводим курсор -> Выводим за контейнер -> -> синюю область нужно скрыть, когда курсор покидает контейнер, при условии, что второй клик еще не совершен. При этом зеленый элемент останется зеленым. Тем самым мы показываем, что пользователь выбрал начальный элемент, но еще не указал конечный, а так как курсор выведен за контейнер, зачем показывать синьку. Роль синей области - визуализация выделения, когда курсор находится в пределах контейнера Если же выделение создано (есть и зеленый и красный элементы), то и синька останется даже если курсор покинет контейнер. |
Teamur,
не понял я что-то, тоесть события mouseover вообще не должно быть? или синяя область должна только быть на том элементе на который наведен курсор? |
Часовой пояс GMT +3, время: 16:35. |