Can't hide other elements while clicking on another - javascript

I'm trying to make a toggle which works, but every element I click on creates a stack of these showed elements. Instead I'm trying to hide everything and display only element that I clicked on. Now I can only hide it when I click on the same element twice, which is not what I want. I want to click on one and hide previous ones that were showing.
.totalpoll-choice-image-2 is a bunch of images that always has to be shown. They are what the user clicks on to display hidden description under each image. That description shows up when I click on .totalpoll-choice-image-2. There are 5 images with that class. The next image I click on, I want to hide the previous description box.
My code:
jQuery(document).ready(function() {
var element = document.getElementsByClassName("totalpoll-choice-image-2");
var elements = Array.prototype.slice.call(Array.from( element ) );
console.log(elements);
jQuery(element).each(function(item) {
jQuery(this).unbind('click').click(function(e) {
e.stopPropagation();
var id = jQuery(this).attr("data-id");
console.log(this);
//jQuery("#" + id).css({"display": 'block !important'});
//document.getElementById(id).style.setProperty( 'display', 'block', 'important' );
var descriptionContainer = document.getElementById(id);
var thiss = jQuery(this);
console.log(thiss);
console.log(jQuery(descriptionContainer).not(thiss).hide());
jQuery(descriptionContainer).toggleClass("show");
});
})
})

You can attach event handlers to a group of DOM elements at once with jQuery. So in this case, mixing vanilla JS with jQuery isn't doing you any favors - though it is possible.
I threw together this little example of what it sounds like you're going for.
The script itself is very simple (shown below). The classes and IDs are different, but the idea should be the same:
// Assign click handlers to all items at once
$('.img').click(function(e){
// Turn off all the texts
$('.stuff').hide();
// Show the one you want
$('#' + $(e.target).data('id')).show();
})
https://codepen.io/meltingchocolate/pen/NyzKMp
You may also note that I extracted the ID from the data-id attribute using the .data() method, and attached the event listener with the .click() method. This is the typical way to apply event handlers across a group of jQuery objects.

From what I understood based on your comments you want to show only description of image that has been clicked.
Here is my solution
$('.container').on('click', 'img', function() {
$(this).closest('.container').find('.image-description').addClass('hidden');
$(this).siblings('p').removeClass('hidden');
});
https://jsfiddle.net/rtsj6r41/
Also please mind your jquery version, because unbind() is deprecated since 3.0

You can use event delegation so that you only add your event handler once to the parent of your images. This is usually the best method for keeping work the browser has to do down. Adding and removing classes is a clean method for show and hide, because you can see what is happening by looking at your html along with other benefits like being easily able to check if an item is visible with .hasClass().
jsfiddle: https://jsfiddle.net/0yL5zuab/17/
EXAMPLE HTML
< div class="main" >
<div class="image-parent">
<div class="image">
</div>
<div class="image-descr">
Some text. Some text. Some text.
</div>
</div>
<div class="image-parent">
<div class="image">
</div>
<div class="image-descr">
Some text. Some text. Some text.
</div>
</div>
<div class="image-parent">
<div class="image">
</div>
<div class="image-descr">
Some text. Some text. Some text.
</div>
</div>
<div class="clear">
</div>
</div>
EXAMPLE CSS
.image-parent{
height: 100px;
width: 200px;
float: left;
margin: 5px;
}
.image-parent .image{
background: blue;
height: 50%;
width: 100%;
}
.image-descr{
display: none;
height: 50%;
width: 100%;
}
.show-descr{
display: block;
}
.clear{
clear: both;
}
EXAMPLE JQUERY
$(".main").on("click", ".image-parent", ShowDescription);
function ShowDescription(e) {
var $parent = $(e.target).parent(".image-parent");
var $desc = $parent.find(".image-descr");
$(".image-descr").removeClass("show-descr");
$desc.addClass("show-descr");
}

Related

Display content related to only those button it is clicked in HTML

I have three buttons in HTML, orders and products and supplier. I want when the user clicks orders order is being shown, when the user clicks products, the product is shown, and the name of the supplier when it is clicked.
function changedata(parameter){
if(parameter==0){
document.getElementById('myorders').style.fontSize="25px";
}
else if(parameter==1){
document.getElementById('myproducts').style.fontSize="25px";
}
else{
document.getElementById('mysupplier').style.fontSize="25px";
}
}
<button class="button" onclick="changedata(0)">ORDERS</button>
<button class="button" onclick="changedata(1)">PRODUCTS</button>
<button class="button" onclick="changedata(2)">SUPPLIER</button>
<div id="myorders">
<p>Laptop, Earphone</p>
</div>
<div id="myproducts">
<p>Earphone, smart watch</p>
</div>
<div id="mysupplier">
<p>Amazon, E-kart</p>
</div>
But it won't hide data and serve my need, I'm a beginner in web development, looking for kind help to show data only when the corresponding button is pressed.
Try giving each element a default value of display:none in your css, as such -
#myorders,
#mysuppliers,
#myproducts {
font-size: 25px;
display: none;
}
This will select each element and hide them right away.
Then, when a button is pressed, you can use
document.getElementById('___').style.display = 'block';
to then show that element.
Here is the final product:
function changedata(parameter){
if(parameter==0){
document.getElementById('myorders').style.display = 'block';
}
else if(parameter==1){
document.getElementById('myproducts').style.display = 'block';
}
else{
document.getElementById('mysupplier').style.display = 'block';
}
}
#myorders,
#myproducts,
#mysupplier{
font-size: 25px;
display: none;
}
<button class="button" onclick="changedata(0)">ORDERS</button>
<button class="button" onclick="changedata(1)">PRODUCTS</button>
<button class="button" onclick="changedata(2)">SUPPLIER</button>
<div id="myorders">
<p>Laptop, Earphone</p>
</div>
<div id="myproducts">
<p>Earphone, smart watch</p>
</div>
<div id="mysupplier">
<p>Amazon, E-kart</p>
</div>
If you would like to have the element toggle between hidden and shown on each button press, I recommend toggling a class with javascript, as such:
function changedata(parameter){
if(parameter==0){
document.getElementById('myorders').classList.toggle('active');
}
else if(parameter==1){
document.getElementById('myproducts').classList.toggle('active');
}
else{
document.getElementById('mysupplier').classList.toggle('active');
}
}
#myorders,
#myproducts,
#mysupplier{
font-size: 25px;
display: none;
}
#myorders.active,
#myproducts.active,
#mysupplier.active{
display: block;
}
<button class="button" onclick="changedata(0)">ORDERS</button>
<button class="button" onclick="changedata(1)">PRODUCTS</button>
<button class="button" onclick="changedata(2)">SUPPLIER</button>
<div id="myorders">
<p>Laptop, Earphone</p>
</div>
<div id="myproducts">
<p>Earphone, smart watch</p>
</div>
<div id="mysupplier">
<p>Amazon, E-kart</p>
</div>
There are slightly easier ways to connect each div to its corresponding button, and one of them is to use data attributes. We can add a data attribute to each button the text of which matches the id of its corresponding div.
(I'm assuming that when you click on one button all the other divs are hidden, and only its div shows.)
This example uses more modern JS techniques but I'll guide you through them, comment everything, and provide documentation at the end. You don't have to understand everything here but you're probably going to bump up against these things eventually, so you might as well take a look at them now.
Here's a rundown of how this all works:
Remove the inline listeners from the buttons. Modern JS uses addEventListener.
Wrap the buttons in a container. What we're going to use is a technique called event delegation. Instead of attaching listeners to every button we attach one to the container and this captures any events that "bubble up" the DOM from its child elements. We can then call a function when a child element is clicked.
The function does a few things. First it checks to see if the clicked element was actually a button. Then it hides all the "panels" by removing a class called "show" from them ("show" sets the element's display to block - initially all panels have their display set to none). Then based on the id from the button's data attribute it forms a selector with it, and we use that to target its corresponding div and apply the "show" class.
// Cache out buttons container, and all of the panels
const buttons = document.querySelector('.buttons');
const panels = document.querySelectorAll('.panel');
// Add an event listener to the buttons container
buttons.addEventListener('click', handleClick);
// When a child element of `buttons` is clicked
function handleClick(e) {
// Check to see if its a button
if (e.target.matches('button')) {
// For every element in the `panels` node list use `classList`
// to remove the show class
panels.forEach(panel => panel.classList.remove('show'));
// "Destructure" the `id` from the button's data set
const { id } = e.target.dataset;
// Create a selector that will match the corresponding
// panel with that id. We're using a template string to
// help form the selector. Basically it says find me an element
// with a "panel" class which also has an id that matches the id of
// the button's data attribute which we just retrieved.
const selector = `.panel[id="${id}"]`;
// Select the `div` and, using classList, again add the
// show class
document.querySelector(selector).classList.add('show');
}
}
.panel { display: none; }
.show { display: block; }
.button { text-transform: uppercase; }
.button:hover { cursor: pointer; background-color: #fffff0; }
<div class="buttons">
<button data-id="myorders" class="button">Orders</button>
<button data-id="myproducts" class="button">Products</button>
<button data-id="mysupplier" class="button">Supplier</button>
</div>
<div class="panel" id="myorders"><p>Laptop, Earphone</p></div>
<div class="panel" id="myproducts"><p>Earphone, smart watch</p></div>
<div class="panel" id="mysupplier"><p>Amazon, E-kart</p></div>
Additional documentation
addEventListener
classList
Destructuring assignment
forEach
matches
querySelector
querySelectorAll
Template string

How do I hide the closest element to the element that was clicked without jQuery?

I have a function that changes the src attribute of an icon when this one is clicked.
I also want it to hide the closest icon of the class fave_icon. I tried the following but it's not working:
function trash(event, trashcan){
event.stopPropagation();
if (trashcan.getAttribute('src') == "Iconos/tacho.png")
{
trashcan.src = "Iconos/warning.png"; //this works ok
var heart = trashcan.closest(".fave_icon");
heart.style.visibility = "hidden"
}
}
Basically I want to hide the closest element with class fave_icon to trashcan.
On the HTML I have this several times:
<button class="accordion">
<div>
<img src="Iconos/heart.png" onclick="fav(event,this);" alt="Fave" class="fave_icon">
</div>
<div>
<img src="Iconos/tacho.png" onclick="trash(event,this);" alt="Delete" class="delete_icon">
</div>
</button>
If fave_icon is a class then you have to place dot (.) before the class name as part of the selector.
Change var heart = trashcan.closest("fave_icon");
To
var heart = trashcan.closest(".fave_icon");
Based on the code and HTML you have provided you can do something like the following:
function trash(event, trashcan){
event.stopPropagation();
if (trashcan.getAttribute('src') == "Iconos/tacho.png"){
trashcan.src = "Iconos/warning.png"; //this works ok
var heart = trashcan.closest('button').querySelector('.fave_icon');
heart.style.visibility = "hidden";
}
}
<button class="accordion">
<div>
<img src="Iconos/heart.png" onclick="fav(event,this);" alt="Fave" class="fave_icon">
</div>
<div>
<img src="Iconos/tacho.png" onclick="trash(event,this);" alt="Delete" class="delete_icon">
</div>
</button>
From the trash icon, you go up a level to the div, select the previousElementSibling to get the heart's div, and then go down a level to the heart image itself.
Because the element is already included in the event target, you don't need to pass this. Or, even better, if you select the trash image first, you can avoid this entirely and use explicit variable names, which are easier to understand and debug.
But inline event handlers are essentially eval inside HTML markup - they're bad practice and result in poorly factored, hard-to-manage code. Seriously consider attaching your events with JavaScript, instead, eg: https://developer.mozilla.org/en/DOM/element.addEventListener
Another problem is that buttons should not have closing tags. Use a container element instead, like a div.
So, try something like this:
document.querySelectorAll('img[src="Iconos/tacho.png"]').forEach(img => {
img.onclick = () => {
const heartImg = img.parentElement.previousElementSibling.children[0];
heartImg.style.visibility = 'hidden';
};
});
<div class="accordion">
<div>
<img src="Iconos/heart.png" alt="Fave" class="fave_icon">
</div>
<div>
<img src="Iconos/tacho.png" alt="Delete" class="delete_icon">
</div>
</div>
you can add a class to the clicked element and use the general sibling combinator if the two items are adjacent.
document.getElementById("hide")
.addEventListener("click", (event) => {
event.target.classList.add('active');
}, false);
#hide.active~.element {
visibility: hidden;
}
#hide {
cursor: pointer;
}
.accordion {
padding: 15px;
background: lightgrey;
border-bottom: 1px solid grey;
}
.accordion div {
color: black;
margin-right: 20px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/icono/1.3.0/icono.min.css" rel="stylesheet" />
<div class="accordion">
<div class="icono-trash" id="hide"></div>
<div class="element icono-heart"></div>
</div>

Jquery will not select button after class has been toggled

I have some jquery that will, when a button is clicked, switch a class from a button to a different class (i.e. on click switch class from #testButton from .first to .second with an image toggle to show it works). The first click works well and it toggles the image, but the second click does not do anything. It seems as if it is not recognizing the new class. Here is a fiddle.
https://jsfiddle.net/myfb44yu/
This is the problematic javascript.
$(document).ready(function(){
$('.first').click(function(){
alert('works');
$('#testButton').toggleClass('first', 'second');
});
$('.second').click(function(){
alert("works");
$('#testButton').toggleClass('second', 'first');
});
});
The interesting thing is that it works when I use an alert() to check but not when I try to change an img src.
Your main issue here is a syntax error in regards to your .toggleClass, but seeing as others have addressed that, I'd like to point out that you should consider re-thinking how you apply your listeners - just as good habit moving forward.
An overview of jQuery Event Bindings
Think of the elements on your page as items in a store. You're an employee, and your manager says "Go put a red tag on anything in the toys department", and so you do. The next day, he puts 10 new toys in the toy department, and says to you "Why don't all the toys have red tags on them?" He then moves one of the toys to the clothing section and asks you, "Why does this item have a red tag on it?" It's simple. You put the red tags on anything in the toys department when he told you to do it - things got moved around afterwards.
The toys in this example would be your .first and .second elements.
This is how jQuery event bindings work - they only apply to elements that satisfied the selector at the time the event was initialized.
So, if you do $('.myClass').click();, then put .myClass on five buttons - none of those buttons will call this function, as they didn't have listeners put on them.
Similarly, if you put a listener on an element using class, but then remove the class from that element, it will maintain the bound event.
The Solution
$(document).on("click", ".first", function() { } );
This is known as event delegation.
In continuing with my analogy from before, this would be the equivalent of skipping tagging the items altogether, and instead just deciding whether or not they're a toy when the customer brings them to the cash register.
Instead of putting the listener on specific elements, we've put it on the entire page. By using ".first" as the second parameter (which takes a selector), the function will only be executed if the element has class first.
Hope this helps.
EDIT: As I was typing, JHecht left a good answer that points out the same issue I outlined above.
N number of elements can have the same class name ,so that's the reason if your trying to search it as $('.classname') returns an array ,so that's the reason your code is not working.class selector
Id is unique,each element should have a single id . In your code button has two id's and for the same button your trying to toggle first and second,you need not have two separate events for first and second
instead you can write as following
check this snippet
$(document).ready(function() {
var firstElements = $('.first')
var first = firstElements[0];
var secondElements = $('.second');
var second = secondElements[0]
$("#testButton").click(function() {
alert('works');
$(this).toggleClass('first').toggleClass('second');
});
});
.first {
color: red;
}
.second {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img src="img/images.jpeg" alt="" id="testImage">
<div id="testDiv">
<button type="button" id='testButton' class='first'>Hi</button>
</div>
Hope it helps
Ho about this solution. Hope it helps!
$(document).ready(function(){
$("#testButton").click(function(){
if($(this).prop("class") === "first"){
alert('first');
$(this).removeClass("first").addClass("second");
}
else if($(this).prop("class") === "second"){
alert("second");
$(this).removeClass("second").addClass("first");
}
});
});
.first{
color: red;
}
.second{
color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<img src="img/images.jpeg" alt="" id="testImage">
<div id="testDiv">
<button type="button" id='testButton' class='first'>Hi</button>
</div>
I hope that what I am about to say makes more sense than I feel it does.
Your issue is that when you assign the click events, there is not currently an element that has a class of .second.
Also, your code is wrong. toggleClass accepts a few arguments, the first is a string of classes, the second is an optional parameter to check whether or not to toggle the classes on or off.
A way to accomplish what you want without changing a whole lot of code is event delegation, shown below.
$(function() {
$(document).on('click', '.btn-first,.btn-second', function() {
//here we are adding the click event on the document object, and telling it that we only want to delegate this event to an object that matches the classes of .btn-first or .btn-second.
//Note: to those saying "why not just do it on the .btn class an avoid having to do this", it is so he can see what delegation looks like. But you are correct, with this markup it would be better to simply add the click event on the .btn class.
$(this).toggleClass('btn-first btn-second');
});
});
.btn {
padding: 4px 8px;
border-radius: 3px;
border: 1px solid grey;
}
.btn-first {
background-color: green;
border-color: green;
}
.btn-second {
background-color: orange;
border-color: orange
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img src="img/images.jpeg" alt="" id="testImage">
<div id="testDiv">
<button type="button" id='testButton' class='btn btn-first'>Hi</button>
</div>
A combination of javascript, CSS and HTML to toggle the class of #testButton when any element of class "first" or "second" is clicked, including the test button itself. The posted code was changed to supply JQuery's .toggleClass method with a space separated list of class names. Click "run snippet" to test the effect.
$(document).ready(function(){
$('.first').click(function(){
$('#testButton').toggleClass('first second');
});
$('.second').click(function(){
$('#testButton').toggleClass('first second');
});
});
.first { border: thick outset green;}
.second { border: thick inset red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="first">This paragraph has first class</p>
<p class="second">This paragraph has second class</p>
<button type="button" id="testButton" class="first">this button starts out first class</div>
The script can then be simplified by combining multiple class names in a single selector, leaving just:
$(document).ready(function(){
$('.first, .second').click(function(){
$('#testButton').toggleClass('first second');
});
});
Make a neutral class that the buttons both share (.btn).
Then add one of the state classes to each button (.first or .second).
Delegate the click event to the neutral class only ($('.btn').on('click',...).
Then toggle both state classes on this ($(this).toggleClass('first second');)
The images change by CSS, each button has 2 images which alternate between display:none/block according to the button's state class.
There is an example with the images outside of buttons and another example that doesn't toggle classes around.
SNIPPET
$('.btn').on('click', function() {
$(this).toggleClass('first second');
});
/* OR */
$('.alt').on('click', function() {
$('.img').toggle();
});
.first > .one {
display: block;
}
.first > .two {
display: none;
}
.second > .one {
display: none;
}
.second > .two {
display: block;
}
.first + .one {
display: block;
}
.first + .one + .two {
display: none;
}
.second + .one {
display: none;
}
.second + .one + .two {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Use jQuery with CSS</p>
<button class='btn first'>
<img src='http://placehold.it/50x50/000/fff?text=1' class='one'>
<img src='http://placehold.it/50x50/fff/000?text=2' class='two'>
</button>
<button class='btn second'>
<img src='http://placehold.it/50x50/0e0/960?text=1' class='one'>
<img src='http://placehold.it/50x50/fff/000?text=2' class='two'>
</button>
<br/>
<br/>
<button class='btn first'>Toggle</button>
<img src='http://placehold.it/50x50/fc0/00f?text=1' class='one'>
<img src='http://placehold.it/50x50/00f/fc0?text=2' class='two'>
<button class='btn second'>Toggle</button>
<img src='http://placehold.it/50x50/fc0/00f?text=1' class='one'>
<img src='http://placehold.it/50x50/00f/fc0?text=2' class='two'>
<p>Or use only jQuery no CSS</p>
<img src='http://placehold.it/50x50/0e0/930?text=1' class='img'>
<img src='http://placehold.it/50x50/930/0e0?text=2' class='img' style='display:none'>
<button class='alt' style='display:block;'>Toggle</button>

jQuery click event seems to be kind of late

I'm working on a Facebook reaction bar so it is pretty hard to copy the code here because it has a lot of events binded but all of you got facebook so if you want to check it by yourself - please do it.
The thing is that I managed to move the reaction bar under the react root and now I wanted to make the clicked reaction counter change the background color of itself to green.
And everything is working almost good excluding one thing: it is one click behind. To make you understand better I recorded little example how it looks. The red pulse ring appears when I click: https://vid.me/HqYp
Here is the changing code:
$(this).find('div._iu-[role="toolbar"]').bind('click',function(){
$(this).find('p.counter').each(function(){$(this).css('background-color','#48649F');});
$(this).find('span[aria-pressed="true"]').find('p.counter').css('background-color','green');
});
$(this) is div[id*="post"] so in $(this) I'm getting div with the whole post.
I thought that maybe I should use a callback function after changing-every-counter-to-default-color function but I don't know am I right and if it's right solution.
Thanks from above. (:
You can probably simplify this a bit. Although without the html structure I can't know for sure how the layout of the function works with respect to the event origin. Also I am not sure when the aria-pressed is set to true so I made the function a bit more generic. You simply add a data attribute to target the span you want to be targeted by the click.
<div class="_lu-" role="toolbar" data-target=".facebook-counter">
Later in your javascript you do the following
var $t = $(this);
var $t.target = $(this).data('target');
$t.on('click','div._lu-[role="toolbar"]', function() {
$t.find($t.target).css({
'background-color':'green'
}).siblings().css({'background-color','#48649F'});
});
This code is assuming first that your spans are in the same container, and second that the first $(this) refers to the parent container of this whole toolbar, and last that you have put data-target="" attributes with selectors for the appropriate target you want to affect.
This is a sample:
$(document).ready(function(){
$('.toolbar').on('click','.toolbar-item .icon', function(event) {
event.preventDefault();
event.stopPropagation();
if(!this.$) this.$ = $(this);
if(!this.parent) this.parent = this.$.parent();
if(!this.counter) this.counter = this.$.siblings('.counter');
this.parent.addClass('selected').siblings('.selected').removeClass('selected');
var count = this.counter.data('value');
count++;
this.counter.data('value',count);
this.counter.html(count);
});
});
.toolbar {
font-size:0;
text-align:center;
}
.toolbar-item .icon {
background:#FFF;
padding:30px;
border:1px solid #AAA;
border-radius:100%;
margin:0 20%;
transition:0.8s ease all;
}
.selected .icon {
background:#369;
}
.toolbar-item .counter {
background:#E0E0E0;
margin:0 10px;
transition:0.4s ease background;
}
.selected .counter {
background:#509050;
}
.toolbar-item {
font-size:10pt;
width:25%;
display:inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="toolbar">
<div class="toolbar-item">
<div class="icon">Like</div>
<div class="counter" data-value="0">0</div>
</div>
<div class="toolbar-item">
<div class="icon">Wow</div>
<div class="counter" data-value="0">0</div>
</div>
<div class="toolbar-item">
<div class="icon">Sad</div>
<div class="counter" data-value="0">0</div>
</div>
<div class="toolbar-item">
<div class="icon">Angry</div>
<div class="counter" data-value="0">0</div>
</div>
</div>
As of jQuery 1.7 they introduced the .on('click', function().... method. Try that instead and see if you get the same results.
Quick answer without having tested or the time to test your code. I recently had a performance issue with a nested function, so maybe look at that second line with the .each() method.

Selecting child of previous parent jQuery

Lately I've been trying my hand at animation using CSS and jQuery, it went well, however, now I want to do a bit more.
That is, once the user clicks information should show up on top of the image.
At the moment, I just have a few tags on which I perform the animations and class toggles.
My question is, I've thought about doing the following:
<div class= "singleImage">
<img src.... class="actualImage">
<p>text to put over the image</p>
</div>
This would be done per image which means that I'll have about 5 of them with different images.
However, I don't know how to go about selecting the previous element of class "actualImage".
Has anyone got any suggestions?
Thank you
Use the jQuery prev function. Example: Assume you want to select the image previous to the second image:
var foo = $(".singleImage").eq(1);
var bar = $(foo).prev().find('.actualImage');
Fiddle
Try this:
$('singleImage').children('.actualImage').prev();
I'm not sure why you are trying to select the previous element, but you could do something akin to this:
Bind a function to the click event for the element containing your image and caption.
Inside this function, toggle the caption.
Also, bind a click event handler to the body to detect clicks "off" the containing element.
HTML:
<a href="#" class="has-caption">
<img src="http://placehold.it/300x300" />
<span class="caption">This is a caption</span>
</a>
CSS:
a.has-caption { position: relative; }
a.has-caption .caption {
background: rgba(0, 0, 0, .25);
bottom: 0;
color: #fff;
display: none;
height: 20px;
left: 0;
line-height: 20px;
position: absolute;
width: 100%;
}
a.has-caption img { vertical-align: bottom }
JavaScript
$('a.has-caption').on('click', function(e) {
e.preventDefault(); e.stopPropagation();
var self = $(this)
, tmpId = 'toggle-' + Date.now();
self.addClass(tmpId);
$('span.caption', self).toggle();
$('body').one('click', function(e) {
if (!$(event.target).closest('.' + tmpId).length) {
$('span.caption', '.' + tmpId).hide();
self.removeClass(tmpId);
};
});
});
http://jsfiddle.net/83s7W/

Categories