简体   繁体   中英

Click 2 elements at the same time?

I have a scenario where a user can choose 1 element or 2 elements to click
If the user choosed 2 elements he can choose 2 elements in a single click but only to its neighbour element
Ex:
If user click on Elem 1 container, container below will be active or clicked
Elem 1 and Elem 2
If user click on Elem 3 container, container below will be active or clicked
Elem 2 and Elem 3

Moreover if user clicked on Elem 2 it will depend on the nearest area where the user clicked(the Y axis ) if it will be partnered with Elem 1 or Elem 3

But NOT Elem 1 to 3 because there is a gap between them(the Elem 2) and as I've mention only the neighbour container.

I am using ReactJS

在此处输入图片说明

<div class="myClass">
  Elem 1
</div>

<div class="myClass">
  Elem 2
</div>

<div class="myClass">
  Elem 3
</div>

Edit 1

Based on the OP's subsequent comments, I realized they also wanted the second selected item to depend on how close the item is to the click position . I made some updates.

See updated React Code Sandbox here


App.jsx

import React, { useState } from "react";
import "./styles.css";

const ElementsList = () => {
  // 1. List of Items, they could be anything
  const items = ["Elem 1", "Elem 2", "Elem 3", "Element 4", "Element 5"];

  // 2. Track the selected items in state
  const [selectedItems, setSelectedItems] = useState([]);

  const handleClick = (e, index) => {
    // 7. Extract the click position and element height from the event target.
    const nextItems = getNextSelections({
      currentIndex: index,
      totalItems: items.length,
      elementHeight: e.currentTarget.getBoundingClientRect().height,
      distanceFromTop: e.clientY - e.currentTarget.getBoundingClientRect().top
    });

    // 4. Do whatever you want with the selected items :), then update state
    setSelectedItems(nextItems);
  };

  return (
    <div className="List">
      {items.map((value, index) => (
        <div
          key={value}
          className={`myClass ${
            // 5. Conditionally set selected class if index is present in selected Items array
            selectedItems.includes(index) ? "selected" : ""
          }`}
          // 6. Capture the click event and the index of the selected item
          onClick={e => handleClick(e, index)}
        >
          {value}
        </div>
      ))}
    </div>
  );
};

export default function App() {
  return (
    <div className="App">
      <h1>2 Items Selector</h1>
      <ElementsList />
    </div>
  );
}

getNextSelections helper


// Helper to get Select items based on mouse position
const getNextSelections = ({
  currentIndex, // Index of the clicked Item
  totalItems, // Total number of items in the list
  elementHeight, // Height of the bounding rectangle of the element
  distanceFromTop // Distance of Mouse click postion from top of boudning rectangle of the element
}) => {
  // A. Return first and second item if first item is clicked, exit early
  if (currentIndex <= 0) return [0, 1];

  // B. Return last and second last if last item is clicked and exit early
  if (currentIndex === totalItems - 1) return [currentIndex - 1, currentIndex];

  // C. If clicked in top half of the element, return previous and current item
  if (distanceFromTop < elementHeight / 2) {
    console.log("Cicked Next to the Top of Element");
    return [currentIndex - 1, currentIndex];
  }

  // D. Otherwise return current and next item indicies
  return [currentIndex, currentIndex + 1];
};

styles.css

.App {
  font-family: sans-serif;
  text-align: center;
}

.List {
  width: 80%;
  margin: 0 auto;
  text-align: center;
}

.myClass {
  padding: 1rem;
  border: solid 4px black;
  border-bottom: none;
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.myClass.selected {
  background: rgb(236, 174, 174);
  transition: background 1s;
}
.myClass:last-child {
  border: solid 4px black;
}


I have added a helper to conditionally check the click position from the top of the selected element's bounding rectangle. Followed these assumptions.

  1. If the first item is clicked, just choose the first and second items. [A]
  2. Choose the last and second last item if the last one is clicked. [B]
  3. For elements in the middle of the list, choose the previous item as the second selection if the click happens in the top half of the clicked item's bounding rectangle [C] , otherwise choose the previous item. [D]

Code Sandbox: Multiple Items Select with Mouse Proximity

Edit 2

In your comments you say the list has rows and columns :( You can extend the helper to account for the horizontal proximity using getBoundingClientRect().left and width , and use the same approach to select the index of items to the side closest to the click.

Edit 3

Had removed the approach where the next selected item doesn't depend on the position of the mouse click, but the code can be found in this Revision 1 of this post here and the Codesandbox "Multiple Items Select without Mouse Proximity"

I have created a solution for this using javascript only

In this approach i have used Event Bubbling Concept too. You can check it out at https://jsfiddle.net/harshapache/dhqnrvwx/


Important methods of DOM (javascript):

  • check your div has class(Eg: active) event.target.classList.contains("active");
  • Next Sibling Element event.target.nextElementSibling
  • Previous Sibling Element event.target.previousElementSibling
  • Add Class event.target.classList.add("active");
  • Remove Class event.target.classList.remove("active");

HTML

  <div id="myClassWrapper">
     <div class="myClass">Elem 1</div>
     <div class="myClass">Elem 2</div>
     <div class="myClass">Elem 3</div>
  </div>

JS

    var selectedElements = 0;
    var myClassWrapper = document.getElementById('myClassWrapper');
        myClassWrapper.addEventListener("click", function(event){
    var isMyClassClicked = event.target.classList.contains("myClass");


    if(isMyClassClicked){
      var isActive = event.target.classList.contains("active");
      if(isActive){
       //Do Unselect
       event.target.classList.remove("active");
       selectedElements--;
      }
      else{
        //Do Select
        //1.Check selectedElemnts not equal to 2
        if(selectedElements == 2) alert('You already selected two elements');
         if(selectedElements == 1){
         //check neighbour has active or not
         var isNextElemActive = event.target.nextElementSibling ? event.target.nextElementSibling.classList.contains("active") : false;
         var isPrevElemActive = event.target.previousElementSibling ? event.target.previousElementSibling.classList.contains("active") : false;
         if(isNextElemActive || isPrevElemActive){
           event.target.classList.add("active");
           selectedElements++;
         }else{
           alert("you can't select it");
         }
        }
        if(selectedElements == 0){
          event.target.classList.add("active");
          selectedElements++;
        }
       }
     }
   });

You can use jQuery's .next() method to find next div.

 $(".myClass").on("click", function() { $('.myClass').css("background","none"); $(this).css("background", "red"); $(this).next(".myClass").css("background", "red"); })
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="myClass"> Elem 1 </div> <div class="myClass"> Elem 2 </div> <div class="myClass"> Elem 3 </div>

I have read your question, so you have 3 elements and all you want is if you click on the above part of the element both above and below one will be highlighted, Same goes for the bottom part if you bottom part of the element both above and below one will be highlighted for this functionality, I have tested a jQuery code

<script>
$(document).ready(function(){
  $(".oneEbottom").on("click", function(event){
    $(event.delegateTarget).css("background-color", "red");
    $(".twoE").css("background-color", "red");
    $(".oneE").css("background-color", "red");
    $(".threeE").css("background-color", "white");
  });

  $(".twoEabove").on("click", function(event){
    $(event.delegateTarget).css("background-color", "red");
    $(".oneE").css("background-color", "red");
    $(".twoE").css("background-color", "red");
    $(".threeE").css("background-color", "white");
  });

  $(".twoEbottom").on("click", function(event){
    $(event.delegateTarget).css("background-color", "red");
    $(".threeE").css("background-color", "red");
    $(".twoE").css("background-color", "red");
    $(".oneE").css("background-color", "white");
  });

  $(".threeEabove").on("click", function(event){
    $(event.delegateTarget).css("background-color", "red");
    $(".threeE").css("background-color", "red");
    $(".twoE").css("background-color", "red");
     (".oneE").css("background-color", "white");
  });
});
</script>

you can use this simple script for this funtionality but you have to link the jQuery file in you tag

<head>

<style>
.a {
    border: 1px solid #000;
}

</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

</head>
<body>






<div class="oneE a" >
<div class="oneEinner">Element 1</div>
<div class="oneEbottom s"><small>to highlight Element 1 & 2</small></div>
</div>

<div class="twoE a">
    <div class="twoEabove s"><small>to highlight Element 1 & 2</small></div>
    <div class="twoEinner">Element 2</div>
    <div class="twoEbottom s"><small>to highlight Element 2 & 3</small></div>
</div>

<div class="threeE a">
<div class="threeEabove s"><small>to highlight Element 2 & 3</small></div>
<div class="threeEinner">Element 3</div>
</div>

As you see for more clarification I put a small text in each section to check the above and bottom when you are going to click on it, you can remove it this is only for testing purpose

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM