如何查找离单击位置最近的子项

How to find nearest child to a click location?

本文关键字:最近 位置 单击 何查找 查找      更新时间:2023-09-26

Fiddle:http://jsfiddle.net/1bgun0k0/1/

我有一个DIV和几个孩子SPAN。

<div id="parent">
    <span class="child">Alpha</span>
    <span class="child">Beta</span>
    <span class="child">Gamma</span>
</div>

子元素有余量:

#parent { padding: 5px; }    
.child { margin: 5px; }

当用户在子元素之间(或在子元素外的父DIV的任一端)单击时,我需要在那里插入一个新的子元素。

如何检测用户单击了哪些子元素?


更新:我需要支持多排孩子(谢谢Roko的提醒)。应忽略行之间的单击。应正确处理整行左右两侧的单击。

由于页边空白不接收点击事件,我可能建议使用5px占位符div而不是页边空白,这样在子级之间点击会导致点击占位符。

单击后,您将在下面插入一个新的子项(和一个新5px占位符)。

更新:由于您将问题从在之间单击改为在旁边单击,因此您可以使用伪元素尝试此技巧(以防止divitis)https://stackoverflow.com/a/23243996/1998238

另一种解决方案是使用CSS来屏蔽您正在单击的区域。你可以这样包装你的元素:

<div id="parent">
    <div id="alpha" class="spacer">
        <span class="child">Alpha</span>
    </div>
    <div id="beta" class="spacer">
        <span class="child">Beta</span>
    </div>
    <div id="gamma" class="spacer">
        <span class="child">Gamma</span>
    </div>    
</div>

这是代码:https://jsfiddle.net/theodin/5enfs52t/2/

点击:

  1. 遍历每个child,根据相对于光标的位置抓住左右两侧的子对象。(检查event.pageY以确保它们在正确的一行。)

  2. 如果找到左侧的子项,请继续迭代到最后。最后一个留守儿童将是真正的留守儿童。

  3. 如果找到右侧的子项,请停止迭代。这就是你想要的。

  4. 如果找到了一个左边的孩子,请插入文本,并在后面插入一个空格

  5. 如果找到合适的子项,请在其前面插入文本一个空格

代码段

var n = 0;
$('#parent').click(function(e) {
  if(e.target.id === 'parent') {
    var parent= $(this),
        firstChild= $(this).find('.child').first(),
        lastChild = $(this).find('.child').last(),
        leftChild,
        rightChild,
        text= function() {return $('<span class="child new">New child '+(++n)+'</span>');}
    parent.find('.child').each(function() {
      var pos= $(this).offset(),
          h= $(this).height();
        
      if(pos.top <= e.pageY && pos.top+h >= e.pageY) {
        if(pos.left <= e.pageX) {
          leftChild= $(this);
        }
        else if(pos.left >= e.pageX) {
          rightChild= $(this);
          return false;
        }
      }
    });
      
    if(leftChild) {
      leftChild.after(text()).after(' ');
    }
    else if(rightChild) {
      rightChild.before(text()).before(' ');
    }
    else if(!firstChild.length || e.pageY < firstChild.offset().top) {
      parent.prepend(text()).before(' ');
    }
    else if(e.pageY > lastChild.offset().top) {
      lastChild.after(text()).after(' ');
    }
    //otherwise, we're between rows
  }
});
body {
  margin: 10px;
  padding: 10px;
}
#parent {
  padding: 5px;
  background-color: green;
  cursor: pointer;
  width: 450px;
  line-height: 2.7em;
}
.child {
  margin: 5px;
  white-space: nowrap;
  background-color: yellow;
  border: 1px solid black;
}
.new {
  background-color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="parent">
  <span class="child">Alpha</span>
  <span class="child">Beta</span>
  <span class="child">Gamma</span>
  <span class="child">Delta</span>
  <span class="child">Epsilon</span>
  <span class="child">Zeta</span>
  <span class="child">Eta</span>
  <span class="child">Theta</span>
  <span class="child">Iota</span>
  <span class="child">Kappa</span>
  <span class="child">Lambda</span>
  <span class="child">Mu</span>
  <span class="child">Nu</span>
  <span class="child">Xi</span>
  <span class="child">Omicron</span>
  <span class="child">Pi</span>
  <span class="child">Rho</span>
  <span class="child">Sigma</span>
  <span class="child">Tau</span>
  <span class="child">Upsilon</span>
  <span class="child">Phi</span>
  <span class="child">Chi</span>
  <span class="child">Psi</span>
  <span class="child">Omega</span>
</div>

http://jsfiddle.net/tukqnujm/2

这是以前建议的,但您可以在其他元素之间添加透明的可点击元素,作为边距。

var parent = $("#parent");
parent.on("click", function(e) {
    if(e.target.classList.contains("between")) {
        var item = document.createElement("span");
        item.classList.add("item");
        var between = document.createElement("span");
        between.classList.add("between");
        e.target.parentNode.insertBefore(item, e.target);
        e.target.parentNode.insertBefore(between, item);
    }
});

但是要小心,如果元素之间有空格,内联块标记会在文档中添加一个空格,而这些空格是不可单击的。这就是为什么我把评论放在jsfiddle中。

   <div id="parent"><!--
    --><span class="between"></span><!--
    --><span class="item"></span><!--
    --><span class="between"></span><!--
--></div>

您可以跟踪光标的位置(因为光标不会立即位于任何.child元素上,因此将单击事件绑定到它是无用的),然后检索该坐标左侧的.child元素列表。获取满足此标准的最后一个.child元素,在其后面添加新元素。

此外,您应该将.on()用于.child的点击事件绑定,因为新添加的元素不会注册点击事件(因为使用现有代码,您在运行时绑定点击事件,而页面上不存在新添加的元件)。

$('.child').on('click', function(e) { 
    e.stopImmediatePropagation();
    $(".debug").removeClass("debug");
    $(this).toggleClass("debug");
})
$("#parent").click(function(e) { 
    e.stopImmediatePropagation();
    $(".debug").removeClass("debug");
    $(this).toggleClass("debug");
    // Get mouse position along x-axis
    var xPos = e.pageX,
        yPos = e.pageY;
    // Get x-axis offset of child
    // Return array of child elements that is to the left of mouseclick
    // and get the last child, append new element after it
    var $prevChild = $('.child').filter(function() {
        return ($(this).offset().left < xPos && $(this).offset().top < yPos)
    }),
        $content = $('<span class="child">New child</span>');
    // Additional logic from @AlexanderGladysh
    if ($prevChild.length > 0) {
        $content.insertAfter($prevChild.last());
    } else {
        $(this).prepend($content);
    }
})

请在此处查看小提琴:http://jsfiddle.net/teddyrised/1bgun0k0/9/