Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Получить дочерний элемент по координатам клика. (https://javascript.ru/forum/misc/61328-poluchit-dochernijj-ehlement-po-koordinatam-klika.html)

Lemme 13.02.2016 13:38

Получить дочерний элемент по координатам клика.
 
Суть такая, есть блок (контейнер), внутри него есть n блоков, так вот, по клику на контейнер в любом месте, нужно получить подходящий элемент.

структура примерно такая:
---------------------------
 | 0 | | 1 | | 2 | | 3 |
---------------------------


Решил я это такм способом.

[].filter.call(this.children, function(item) {
     return item.getBoundingClientRect().left <= event.clientX;
}).pop();


Собственно, код работает, но, что-то мне подсказывает, что хоть способ и рабочий, фильтровать массив и вытаскивать с него последний элемент не лучший способ..

Есть у кого-то идеи?

Вот полный код.

<style>
	div {
	  display: flex;
	  justify-content: space-between;
	  padding: 30px;
	  background-color: #fff;
	  border: 1px solid #999;
	  cursor: pointer;
	}

	span {
	  width: 100px;
	  height: 100px;
	  background-color: tomato;
	  color: #fff;
	  text-align: center;
	  font-size: 80px;
	  -moz-user-select: none; /* fucking double click :D */
	}
</style>

<div>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<script>
	'use strict';

	const div = document.querySelector('div');

	div.addEventListener('click', handleClick);

	function getPos(item) {
		return item.getBoundingClientRect().left;
	}

	function handleClick(e) {
		const items = Array.from(this.children);
	  
	  const filtredItems = items.filter(item => 
	  	item.getBoundingClientRect().left <= e.clientX
	  );
	  
	  const item = filtredItems.pop() || items[0];
	  
	  // clear items
	  items.forEach(item => {
	  	item.innerHTML = '';
	  });
	  
	  // active item
	  item.innerHTML = '+';
	}
</script>

рони 13.02.2016 14:27

Lemme,
решение http://javascript.ru/forum/misc/6102...tml#post405988 ... чуть добавить фильтр

Lemme 13.02.2016 14:38

рони, спасибо, интересно=)

destus 13.02.2016 15:17

Через offsetLeft никак? А потом через array.some найти нужный. Будет работать быстрее, т.к. не придется по всем элементам массива бегать.
var x = e.clientX;
                 var span = document.querySelectorAll('span');
                 var temp = span[0];
                 [].some.call(span, function (elem) {
                     if (elem.offsetLeft > x) {
                         return true;
                     }
                     temp = elem;
                 })

рони 13.02.2016 15:32

Ближайший элемент от места клика
 
Lemme,
<!DOCTYPE HTML>

<html>

<head>
  <title>Untitled</title>
  <meta charset="utf-8">
  <style>
	div {
	  display: flex;
	  justify-content: space-between;
	  padding: 30px;
	  background-color: #fff;
	  border: 1px solid #999;
	  cursor: pointer;
	}

	span {
	  width: 100px;
	  height: 100px;
	  background-color: tomato;
	  color: #fff;
	  text-align: center;
	  font-size: 80px;
	  -moz-user-select: none; /* fucking double click :D */
	}
</style>
</head>

<body>


<div>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
  <span></span>
</div>

<script>
window.addEventListener("DOMContentLoaded", function() {
    var b = document.querySelector("div"),
        d = b.querySelectorAll("span"),
        c;
    b.addEventListener("click", function(b) {
        c = [].map.call(d, function(a) {
            var e = a.getBoundingClientRect(),
                c = e.left + a.offsetWidth / 2 - b.clientX;
            a = e.top + a.offsetHeight / 2 - b.clientY;
            return Math.sqrt(c * c + a * a)
        });
        var f = Math.min.apply(null, c);
        [].forEach.call(d, function(a, b) {
            a.innerHTML = c[b] > f ? "" : "+"
        })
    })
});
</script>

</body>

</html>

destus 13.02.2016 15:42

<style>
        div {
	  display: flex;
	  justify-content: space-between;
	  padding: 30px;
	  background-color: #fff;
	  border: 1px solid #999;
	  cursor: pointer;
	}

	span {
	  width: 100px;
	  height: 100px;
	  background-color: tomato;
	  color: #fff;
	  text-align: center;
	  font-size: 80px;
	  -moz-user-select: none; /* fucking double click :D */
	}
    </style>

    <div>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
    </div>
<script>
var div = document.querySelector('div');
             div.addEventListener('click', function (e) {
                 var x = e.clientX;
                 var span = document.querySelectorAll('span');
                 var temp = span[0];
                 [].some.call(span, function (elem, i) {
                     if (elem.offsetLeft > x) {
                         return true;
                     }
                     temp = elem;
                 });
                 [].forEach.call(span,function(elem){
                     (elem == temp) ? elem.innerHTML = '+' : elem.innerHTML = '';
                 })
             })
</script>

рони 13.02.2016 15:44

Lemme,
вопрос только - можно кликнуть так что минимальное растояние будет одинаковое для нескольких элементов, тогда как?

рони 13.02.2016 15:46

destus,
не работает если брать минимальное растояние -- ваш вариант для ближайшего слева

destus 13.02.2016 15:47

Цитата:

Сообщение от рони (Сообщение 407634)
destus,
ваш вариант для ближайшего слева

а надо как? Определять по координатам к какому блоку ближе отношусь и его помечать "+" ?

рони 13.02.2016 16:46

Цитата:

Сообщение от destus
Определять по координатам к какому блоку ближе отношусь и его помечать "+" ?

я понял так, подождём Lemme, :)

destus 13.02.2016 17:03

:) Окей. Для горизонтали.

<style>
        div {
	  display: flex;
	  justify-content: space-between;
	  padding: 30px;
	  background-color: #fff;
	  border: 1px solid #999;
	  cursor: pointer;
	}

	span {
	  width: 100px;
	  height: 100px;
	  background-color: tomato;
	  color: #fff;
	  text-align: center;
	  font-size: 80px;
	  -moz-user-select: none; /* fucking double click :D */
	}
    </style>

    <div>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        <span></span>
    </div>
<script>
var div = document.querySelector('div');
             div.addEventListener('click', function (e) {

                 var x = e.clientX;
                 var span = document.querySelectorAll('span');
                 var temp = span[0];
                 [].some.call(span, function (elem) {
                     if (elem.offsetLeft > x) {
                         var rightCoordBlock = temp.offsetLeft + temp.offsetWidth;
                         var lenBetweenBlock = elem.offsetLeft - (rightCoordBlock);
                         if ((rightCoordBlock + parseInt(lenBetweenBlock / 2)) < x) temp = elem;
                         return true;
                     }
                     temp = elem;
                 });                


                 [].forEach.call(span,function(elem){
                     (elem == temp) ? elem.innerHTML = '+' : elem.innerHTML = '';
                 })
             })
</script>

destus 13.02.2016 17:11

Оптимальнее будет вычислять растояния от центра каждого блока до точки (e.clientX, e.clientY). Минимальное расстояние из всех прямых и будет ответом в задаче.

Или, например, так как все блоки имеют одинаковую ширину и отступы между, то можно вычислить ширину, которую занимает такой элемент на странице (ширина блока + правый отступ). Далее целочисленно делим e.clientX на эту ширину и получаем сколько блоков нам нужно пропустить, чтобы получить нужный.

Вариантов вагон :D

рони 13.02.2016 17:15

Цитата:

Сообщение от destus
Оптимальнее будет вычислять растояния от центра каждого блока до точки (e.clientX, e.clientY). Минимальное расстояние из всех прямых и будет ответом в задаче.

ой ... пост 5 не оно разве?

destus 13.02.2016 17:16

Цитата:

Сообщение от рони (Сообщение 407642)
ой ... пост 5 не оно разве?

a,b,c,d... тяжело все это читается :) Но кажется да.

рони 13.02.2016 17:19

Цитата:

Сообщение от destus
Минимальное расстояние из всех прямых и будет ответом в задаче.

пока неизвестно :) может быть первый элемент с таким растоянием, так будет точнее

destus 13.02.2016 17:23

Даже 5 пост можно немного оптимизировать. Не записывать всё в новый массив и находить минимум, а использовать Array.reduce.

Lemme 13.02.2016 18:19

Цитата:

вопрос только - можно кликнуть так что минимальное растояние будет одинаковое для нескольких элементов, тогда как?
Ну, по сути, минимального расстояния вообще не будет.
Разная может быть ширина блоков и высота контейнера.

<style>
.container {
	padding: 50px 0;
	display: flex;
	background-color: #fff;
	border: 1px solid #999;
}
.block {
	height: 50px;
	background-color: tomato;
	border: 1px solid #333;
}
</style>
<div class="container">
	<div class="block" style="width: 35%"></div>
	<div class="block" style="width: 45%"></div>
	<div class="block" style="width: 20%"></div>
</div>

рони 13.02.2016 19:55

Lemme, ближайший блок к клику дубль 2
<!DOCTYPE HTML>

<html>

<head>
  <title>Untitled</title>
  <meta charset="utf-8">

</head>

<body>


<style>
.container {
	padding: 50px 0;
	display: flex;
	background-color: #fff;
	border: 1px solid #999;
}
.block {
	height: 50px;
	background-color: tomato;
	border: 1px solid #333;
}
</style>
<div class="container">
	<div class="block" style="width: 35%"></div>
	<div class="block" style="width: 45%"></div>
	<div class="block" style="width: 20%"></div>
</div>


<script>
window.addEventListener("DOMContentLoaded", function() {
    var d = document.querySelector(".container"),
        e = d.querySelectorAll(".block"),
        c;
    d.addEventListener("click", function(b) {
        c = [].map.call(e, function(a) {
            a = a.getBoundingClientRect();
            c = a.left > b.clientX ? a.left : a.right < b.clientX ? a.right : b.clientX;
            c -= b.clientX;
            a = a.top > b.clientY ? a.top : a.bottom < b.clientY ? a.bottom : b.clientY;
            a -= b.clientY;
            return Math.sqrt(c * c + a * a)
        });
        var d = Math.min.apply(null, c);
        [].forEach.call(e, function(a, b) {
            a.innerHTML = c[b] > d ? "" : "+"
        })
    })
});
</script>

</body>

</html>

Lemme 13.02.2016 20:08

рони, круто, универсально, но, ад =)))

p.s может какие-то книжки с подобнымыми манипуляциями кто-то всречал?=)

рони 13.02.2016 21:08

Цитата:

Сообщение от Lemme
может какие-то книжки с подобнымыми манипуляциями

Расстояние от точки до прямой на плоскости и в пространстве – определение и примеры нахождения.

Lemme 13.02.2016 21:48

рони,круто,спасибо=)

Блин, зачем эти лимиты на плюсы ставить=)

рони 14.02.2016 00:21

Визуализация работы скрипта ближайший от клика
 
Lemme,
кликая можно увидеть какой блок скрипт выбрал ближайшим и какое место на этом блоке ближе всего к точке клика :)
<!DOCTYPE HTML>

<html>

<head>
  <title>Untitled</title>
  <meta charset="utf-8">

</head>

<body>


<style>
.container {
	  display: flex;
	  justify-content: space-between;
	  padding: 120px;
	  background-color: #FF8C00;
	  border: 1px solid #999;
	  cursor: pointer;
	}

	span {
	  cursor: default;
	  padding: 0;
	  width: 100px;
	  height: 100px;
	  background-color: #32CD32;
	  color: #fff;
	  text-align: center;
	  font-size: 80px;
	  -webkit-user-select: none;
	  user-select: none;
	  -moz-user-select: none; /* fucking double click :D */
	}

.arrow {
  -webkit-transform-origin: 0;
  -moz-transform-origin: 0;
  -o-transform-origin: 0;
  transform-origin: 0;
  position:  absolute;
  }
  .arrow:before,
  .arrow:after {
    content: "";
    display: block;
    position: absolute;
    width: 0;
    height: 0;
    border: 40px solid transparent;
    border-right: 0;
    }
  .arrow {
  width: 0px;
  height: 2px;
  background: #0000CD;
  }
  .arrow:after {
    top: -5px;
    right: 0px;
    border-left-color: #0000CD;
    border-width: 6px 0 6px 30px;
    }
  .arrow.replay:before,
  .arrow.replay:after {
    display: none;

    }
  .arrow.replay {
   border-radius: 50%;
   border: 2px solid #F0FFF0;
   width: 30px;
   height: 30px;
   margin-left: -15px;
   margin-top: -15px;

   background-color: transparent ;

  -webkit-animation: ripple 2s  ease-in-out infinite;
  -moz-animation: ripple 2s  ease-in-out infinite;
  -o-animation: ripple 2s  ease-in-out infinite;
  animation: ripple 2s  ease-in-out infinite;
}
 @keyframes ripple {
  0% {
    opacity: 0;
    -webkit-transform: scale(0);
    -ms-transform: scale(0);
    -o-transform: scale(0);
    -moz-transform: scale(0);
    transform: scale(0);
    margin-left: 0px;

  }
  100% {
    margin-left: -15px;

    opacity: 1;
    -webkit-transform: scale(1);
    -ms-transform: scale(1);
    -o-transform: scale(1);
    -moz-transform: scale(1);
    transform: scale(1);
  }
}
 @-webkit-keyframes ripple {
  0% {
    opacity: 0;
    -webkit-transform: scale(0);
    -ms-transform: scale(0);
    -o-transform: scale(0);
    -moz-transform: scale(0);
    transform: scale(0);
  }
  100% {
    opacity: 1;
    -webkit-transform: scale(1);
    -ms-transform: scale(1);
    -o-transform: scale(1);
    -moz-transform: scale(1);
    transform: scale(1);
  }
}</style>
<div class="container">
  <span></span>
  <span></span>
  <span></span>
</div>
<div class="arrow"></div>

<script>
window.addEventListener("DOMContentLoaded", function() {
    var e = document.querySelector(".container"),
        h = e.querySelectorAll("span"),
        d = document.querySelector(".arrow"),
        c;
    e.addEventListener("click", function(b) {
        var g = [];
        c = [].map.call(h, function(a) {
            a = a.getBoundingClientRect();
            c = a.left > b.clientX ? a.left : a.right < b.clientX ? a.right : b.clientX;
            c -= b.clientX;
            a = a.top > b.clientY ? a.top : a.bottom < b.clientY ? a.bottom : b.clientY;
            a -= b.clientY;
            g.push(360 - 180 / Math.PI * Math.atan2(a, c));
            return Math.sqrt(c * c + a * a)
        });
        var f = Math.min.apply(null,c),
            e = c.indexOf(f);
        d.style.left = b.clientX + "px";
        d.style.top = b.clientY + "px";
        d.style.width = (f ? f : 30) + "px";
        f ? (d.classList.remove("replay"),
        d.style.transform = "rotate(-" + g[e] + "deg)",
        d.style.webkitTransform = "rotate(-" + g[e] + "deg)") : d.classList.add("replay")
    })
});
</script>

</body>

</html>

Lemme 14.02.2016 02:48

рони, это великолепно, карл!! =)
Пост в закладки!! =)

p.s нужно учить математику=)))

Пошел снова тыкать плюсик в оффтопик=))).


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