Detect click outside divs with particular class and hide them - javascript

My DOM looks like this:
<div class = filter x>
<div class = a>
<ul><li>……..</li></ul>
</div>
</div>
<div class = filter y>
<div class = a>
<ul><li>……..</li></ul>
</div>
</div>
Both the divs with class a are dropdown menus. The expected functionality is:
Whenever I click on divs with class filter, js-active class is added
and the dropdown opens.
Whenever I click anywhere outside the div or
the dropdown, js-active class is removed and the dropdown hides.
At a time, only one out of the two dropdowns will be open.
Another event that I am handling is that if one dropdown is visible and I click on another dropdown, first one hides (remove class js-active)
I am able to achieve this with the following code:
$(document).on('mouseup touchend', function(e){
var xContainer = $(‘.filter x’);
var yContainer = $(‘.filter y’);
if (!xContainer.is(e.target) && xContainer.has(e.target).length === 0)
{
xContainer.removeClass('js-active');
}
if (!yContainer.is(e.target) && yContainer.has(e.target).length === 0)
{
yContainer.removeClass('js-active');
}
});
I want to optimize this code. I tried using jQuery "each" to iterate over all the divs with class filter and use the same logic but it didn’t work as expected. Any suggestions would be helpful. Thanks.

There is no need to handle both x & y separately, you can do something like
$(document).on('mouseup touchend', function(e) {
var $filter = $(e.target).closest('.filter');
if ($filter.length) {
$filter.addClass('active');
}
$('.filter.active').not($filter).removeClass('active');
});
.filter {
min-height: 20px;
background-color: lightgrey;
margin-bottom: 5px;
}
.filter .a {
display: none;
}
.filter.active .a {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="filter x">
<div class="a">
<ul>
<li>……..</li>
</ul>
</div>
</div>
<div class="filter y">
<div class="a">
<ul>
<li>……..</li>
</ul>
</div>
</div>
Some other content

After trying more, I got this to work too:
$(document).on('mouseup touchend', function(e){
$('.filter').each(function(){
if(!$(this).is(e.target) && $(this).has(e.target).length === 0)
{
$(this).removeClass('js-active');
}
});
});
Works on all devices.

Related

How can I use this if i'm using two class together using jquery?

I have to select two classes but there is one issue if I want to use this just for single class how can I use that? Like given below:-
$('.city-selector ul li, .city-selector-close').click(function() {
var selectedCity = $(this).text();
$('.selected-city span').text(selectedCity);
})
.city-selector-close {
float: right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="city-selector-close">X</div>
<div class="city-selector">
<ul>
<li class="active">Gurgaon</li>
<li>Delhi</li>
<li>Noida</li>
<li>Faridabad</li>
</ul>
</div>
<div class="selected-city"><span>Gurgaon</span></div>
in above example everything working fine like which city I want to select. I'm able to select but there is one issue when I select two class together .city-selector ul li, .city-selector-close and I just want to get text of clicked class
Still everything working fine, but when I click on close then it will be also working like selected city how to solve this issue with this approach?
It sounds like what you're trying to describe are two separate click handlers. One for clicking the cities, one for clicking the X:
$('.city-selector ul li').click(function() {
var selectedCity = $(this).text();
$('.selected-city span').text(selectedCity);
})
$('.city-selector-close').click(function() {
$('.selected-city span').text('');
})
.city-selector-close {
float: right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="city-selector-close">X</div>
<div class="city-selector">
<ul>
<li class="active">Gurgaon</li>
<li>Delhi</li>
<li>Noida</li>
<li>Faridabad</li>
</ul>
</div>
<div class="selected-city"><span>Gurgaon</span></div>
It's not necessary, or even wise, to condense everything into a single click handler or a single function. Your elements do two different things, so have two different functions for them.
If You want to handle this you can create 2 different event handlers
$('.city-selector ul li').click(function() {
var selectedCity = $(this).text();
$('.selected-city span').text(selectedCity);
})
$('.city-selector-close').click(function() {
alert("close");
})
.city-selector-close {
float: right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="city-selector-close">X</div>
<div class="city-selector">
<ul>
<li class="active">Gurgaon</li>
<li>Delhi</li>
<li>Noida</li>
<li>Faridabad</li>
</ul>
</div>
<div class="selected-city"><span>Gurgaon</span></div>
Or if you want to use the same way as your code you can use the If condition and seperate it.
$('.city-selector ul li , .city-selector-close').click(function() {
var selectedCity = $(this).text();
if(selectedCity == "X"){
alert("close");
}else{
$('.selected-city span').text(selectedCity);
}
})
.city-selector-close {
float: right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="city-selector-close">X</div>
<div class="city-selector">
<ul>
<li class="active">Gurgaon</li>
<li>Delhi</li>
<li>Noida</li>
<li>Faridabad</li>
</ul>
</div>
<div class="selected-city"><span>Gurgaon</span></div>
I would favour two separate click handlers too, but if you realllly want to use a single one, you could check the class of the clicked element using the hasClass function:
$('.city-selector ul li, .city-selector-close').click(function() {
if (!$(this).hasClass('city-selector-close')) {
var selectedCity = $(this).text();
$('.selected-city span').text(selectedCity);
}
});
But echoing what the other answerers have said, it makes much more sense to split this functionality out into two separate click handlers.
You can retrieve the classname of the element that caused the click event using
$(this).attr('class')
So inside the callback function check if it's city-selector-close and in case it is not display the text.
$('.city-selector ul li, .city-selector-close').click(function() {
if ($(this).attr('class') != "city-selector-close") {
var selectedCity = $(this).text();
$('.selected-city span').text(selectedCity);
}
})
.city-selector-close {
float: right;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="city-selector-close">X</div>
<div class="city-selector">
<ul>
<li class="active">Gurgaon</li>
<li>Delhi</li>
<li>Noida</li>
<li>Faridabad</li>
</ul>
</div>
<div class="selected-city"><span>Gurgaon</span></div>

JS toggle for multiple div elements

What should I do if I have multiple elements in HTML foreach and I need to make them all a toggle slider what opens div block with specific information about the element and I need to add close button too if a user wants to close the div. Sorry, I don't have any code to show because I did not find anything that suits my needs. The main idea is to have a product page with products that are displayed on a page using foreach... Then when you click on a product toggle div block is opened with information about a product. What should I search and what to use, I can't find anything because I am limited with my knowledge. Sorry for terrible English.
You can easily control the visibility of an element either within the div you're clicking or after it using a class you toggle. Here's an example controlling the div after one of the divs that controls the toggle:
document.getElementById("container").addEventListener("click", function(e) {
var toggleDiv = e.target.closest(".toggle");
if (toggleDiv && this.contains(toggleDiv)) {
toggleDiv.classList.toggle("closed");
}
});
.closed + .detail {
display: none;
}
<div id="container">
<div class="toggle closed">Product A</div>
<div class="detail">Details about Product A</div>
<div class="toggle closed">Product B</div>
<div class="detail">Details about Product B</div>
<div class="toggle closed">Product C</div>
<div class="detail">Details about Product C</div>
</div>
The key bits there are:
Hiding the details via CSS with the adjacent sibling combinator (+)
Toggling the class on the toggling div
I used event delegation to hook up the handler, but you could instead hook it up to each individual div if you preferred. Note that the Element#closest method I used is relatively new, you may need a polyfill or a loop on parentNode instead.
From what i have understood.
You need to toggle div elements using button tag and the button u click will show that particular div element.
<div id="container1" class={container1 ? "show" : "hide"}>
container1
</div>
<div id="container2"class={container2 ? "show" : "hide"}>
container2
</div>
<div id="container3"class={container3 ? "show" : "hide"}>
container3
</div>
and three button tags to call toggle function to show the three div element.
<div class="container">
<button name="container1" onclick=(toggle())>Container1</button>
<button name="container2" onclick=(toggle())>Container2</button>
<button name="container3" onclick=(toggle())>Container3</button>
</div>
toggle function
function toggle(e) {
if(e.target.name === 'container1'){
container1 = true;
}
else if(e.target.name === 'container2'){
container2 = true;
}
else if(e.target.name === 'container3'){
container3 = true;
}
}
css part
.show{
display: block;
}
.hide{
display: none;
}

displaying a group of divs by clicking next and back buttons

I have 5 divs that contain copy.
I have a back and next button, to display each div.
Back | Next
<div class="vote-result first">
this is example copy
</div>
<div class="vote-result">
this is some more copy
</div>
<div class="vote-result">
some more copy
</div>
<div class="vote-result">
this is the last div
</div>
// hide the divs, except the first one.
.vote-result { display: none; }
.vote-result.first { display: block; }
The first time the next button link is clicked, I want to remove the off class, to show it as clickable, I probably should disable the link initially too and re-enable it too.
$(".back-btn").removeClass("off");
Once I display the last div, I need to add the off class to the next-btn and disable it.
I thought about using a carousel js plugin to accomplish this, but it is overkill for now.
I know of a way to do this, but it would involve assigning subclasses to the links based on what next or back button was clicked, so it will know what div to show next, as well as removing or adding the off class to the links.
I am hoping to find a solution that allows me to add more div's to display without modifying the code. Any help is appreciated, thank you.
Here is solution for you. I have created Fiddle for your requirement.
HTML code:
<a class="back-btn off">Back</a> | <a class="next-btn">Next</a>
<div class="vote-result first selectedDiv">
this is example copy
</div>
<div class="vote-result">
this is some more copy
</div>
<div class="vote-result">
some more copy
</div>
<div class="vote-result last">
this is the last div
</div>
JS/JQuery Code:
$(".back-btn").click(function(){debugger;
var prevElement=$('.selectedDiv').prev();
prevElement.show();
$(".selectedDiv").hide();
$(".selectedDiv").removeClass("selectedDiv");
prevElement.addClass("selectedDiv");
if($('.first').css('display')=="block"){
$(".back-btn").addClass("off");
}
else{
$(".next-btn").removeClass("off");
}
});
$(".next-btn").click(function(){debugger;
var nextElement= $('.selectedDiv').next();
nextElement.show();
$(".selectedDiv").hide();
$(".selectedDiv").removeClass("selectedDiv");
nextElement.addClass("selectedDiv");
if($('.last').css('display')=="block"){
$(".next-btn").addClass("off");
}
else{
$(".back-btn").removeClass("off");
}
});
CSS code:
.vote-result { display: none; }
.vote-result.first { display: block; }
.off{display:none;}
Your HTML file:
<html>
<head>
<style>
.vote-result { display: none; }
.vote-result.first { display: block; }
.off {
color: Red;
}
a {
color: blue;
}
</style>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="code.js"></script>
</head>
<body>
Back | Next
<div class="vote-result first">
this is example copy
</div>
<div class="vote-result">
this is some more copy
</div>
<div class="vote-result">
some more copy
</div>
<div class="vote-result">
this is the last div
</div>
</body>
</html>
Your new "code.js" file in the same directory:
/**
* The zero-based index of the <div class="vote-result"> element that is currently being shown
* #var {Number}
*/
var activeIndex = 0;
function getNumberOfItems() {
return $('.vote-result').length;
}
function synchronizeInterface() {
var numberOfItems = getNumberOfItems(),
lastIndex = numberOfItems - 1;
$('.vote-result').removeClass('first');
$('.vote-result').each(function(index) {
if (index == activeIndex) {
$(this).addClass('first');
}
})
$('.back-btn').toggleClass('off', activeIndex == 0);
$('.next-btn').toggleClass('off', activeIndex == lastIndex);
}
$(function() {
$('.back-btn,.next-btn').on('click', function() {
// If the button clicked is not deactivated
if (!$(this).hasClass('off')) {
// Determine whether the "Next" button was clicked (otherwise "Back" was clicked)
var clickedNext = $(this).hasClass('next-btn');
// Move the active index in the appropriate direction while not allowing it to fall outside the boundaries of appropriate indices
activeIndex = clickedNext
? Math.min(activeIndex + 1, getNumberOfItems() - 1)
: activeIndex = Math.max(0, activeIndex - 1);
// Make sure the interface now reflects the updated JavaScript variables
synchronizeInterface();
}
return false;
});
});
Some notes: You had an unclosed double-quote for one of your class attributes in your provided HTML. Also, I added some additional styling -- you may want to rename the ".first" CSS class to ".active" instead.
take a look at jquerys .next() function for navigating - jQuery - Next(). you can also check for the last item like this.
if($(this).is(':last-child'))
{
$('.next-btn').removeClass('off');
}else{
$('.next-btn').addClass('off');
}
check everytime a navigation button is clicked and do the same for the first button

I can't get all the div's to show up with the same #id

http://jsfiddle.net/bDQt7/4/
This doesn't work, hello2 and hello3 won't show up. It has to do with the '#id can only be used once' ? Changing it to class doesn't work, how to fix this?
HTML
Toggle
<div id="menu" class="hidden">hello</div>
<div id="menu" class="hidden">hello2</div>
<div id="menu" class="hidden">hello3</div>
CSS
.hidden {
display: none;
}
.unhidden {
display: block;
}
JS
function unhide(divID) {
var item = document.getElementById(divID);
if (item) {
item.className = (item.className == 'hidden') ? 'unhidden' : 'hidden';
}
}
IDs must be unique.
Try this:
HTML:
<div class="hidden">hello</div>
<div class="hidden">hello2</div>
<div class="hidden">hello3</div>
JS:
$(document).ready(function(){
$("a").click(function(){
$("div").toggleClass("hidden unhidden");
});
});
Fiddle here.
A Jquery solution for you, I just replaced your id with another unique class. Just refer the code below to get a grip over it.
HTML
Toggle
<div class="xTest hidden">hello</div>
<div class="xTest hidden">hello2</div>
<div class="xTest hidden">hello3</div>
JQUERY
$("a").click(function(){
var xObj = $(".xTest");
if(xObj.hasClass("hidden"))
{
xObj.removeClass("hidden").addClass("unhidden");
}
else
{
xObj.removeClass("unhidden").addClass("hidden");
}
});
DEMONSTRATION
Why don't your wrap all your divs inside another div?
http://jsfiddle.net/bDQt7/7/
it has more sense to have menu and items inside it (I guess you need anchors and not divs inside the menu div)
That way you don't need jquery if you still don't know what it is.
<div id='menu' class='hidden'>
<a href='#'>menu</a>
<a href='#'>menu2</a>
<a href='#'>menu3</a>
</div>

Hiding/showing div's children in jQuery

In my site, I have a set of divs the have a delete option (which is basically a link). What I want is that the delete option -that is hidden by default- to be shown when the mouse enters the div and hidden when it leaves it. And if a div has children and the mouse is over one of it's children, then both the container and the child should have the delete option visible.
Here's the HTML:
<div class="container">
Delete
<div class="child">
Delete
</div>
<div class="child">
Delete
</div>
.
.
.
<div class="child">
Delete
</div>
</div>
So here's the JavaScript I've come up with:
$('div.container').mouseenter(function () {
$(this).find('a.deleteOption').show();
$(this).find('div.child').find('a.deleteOption').hide();
});
$('div.container').mouseleave(function () {
$(this).find('a.deleteOption').hide();
});
$('div.child').mouseenter(function () {
$(this).find('a.deleteOption').show();
$(this).parent.find('a.deleteOption').hide();
});
$('div.child').mouseleave(function () {
$(this).find('a.deleteOption').hide();
});
This works fine in Chrome and Firefox, but in IE (even in IE 9) the delete option is intermittently shown and hidden. Is there a better way to do this using jQuery for every browser?
You can achive this with pure css:
.container .deleteOption { display:none; }
.container:hover > .deleteOption { display:inline; }
.child:hover > .deleteOption { display:inline; }

Categories

Resources