Reputation: 1120
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>
Upvotes: 3
Views: 1770
Reputation: 1774
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
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>
);
}
// 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];
};
.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.
[A]
[B]
[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"
Upvotes: 2
Reputation: 443
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):
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++;
}
}
}
});
Upvotes: 1
Reputation: 21
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
Upvotes: 0
Reputation: 7949
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>
Upvotes: 0