each loop should only run once when button is clicked - javascript

I want to hide a div when a button is clicked and then show the next div with the same class. I have tried using this code, but when I do this, the next div that I want shown also fades out.
$('.arrow:last-of-type').on('click', function(){
$( ".collaboration" ).each(function( index ) {
if($('.collaboration').css('display') == 'block'){
$(this).fadeOut()
$(this).next().fadeIn()
}
})
})
Is there a way to stop the each() from running when it is executed once and then run again when the click action happens?
Here's the HTML:
<div id="collaborations">
<p class="arrow"><</p>
<div class="collaboration">
<img src="./img/test.png" />
<p>Text</p>
<button>Visit</button>
</div>
<div class="collaboration">
<img src="./img/test.png" />
<p>Text</p>
<button>Visit</button>
</div>
<div class="collaboration">
<img src="./img/test.png" />
<p>Text</p>
<button>Visit</button>
</div>
<p class="arrow">></p>
</div>
</div>
Thanks

Solution
You could solve this by not iterating through the .collaborations at all.
The key thing is that you need to keep track of which one is currently being shown.
If you know that, then what your click handler can do is show the next one and hide the current one.
I would suggest doing that with a class .active on the same div as .collaboration. You can then select the next div by $('.active').next().addClass('.active'), and deselect by $('.active').removeClass('.active').
You might need to store a reference to your first element before you select the next one 👍
Example
Here's a quick example of how this might work: https://codepen.io/juancaicedo/pen/LYGgPWa
I moved around the html to group all the collaborations into a div by themselves.
Other approaches
You'll find that you have to think through some other behaviors with the solution above. For example, in my example, there is a moment when two items are on the screen, causing their container div to grow and later shrink.
For these reasons, I don't like handling presentation from within javascript (i.e. using jquery's fadeIn/fadeOut).
If you can find a way to instead using only css, I think that's preferable. Here's an example using css transitions
https://codepen.io/juancaicedo/pen/ZEQqzrR

The each method of jQuery can be stopped whenever you want by returning false inside of the callback, so you can probably fix your code by doing this:
$('.arrow:last-of-type').on('click', function(){
$( ".collaboration" ).each(function( index ) {
if($('.collaboration').css('display') == 'block'){
$(this).fadeOut()
$(this).next().fadeIn()
return false;
}
});
});

Related

Finding the next type of element after a selector in jquery

I am hoping to get some help with finding the next type of an element after finding an image with ends a specific way. So far, I've got it locating the images and beginning to run an each() function, but I can't get it to select the next input on the page.
Here's the code I have so far:
$("img[src$='sunglasses.jpg']").each(function( index ) {
$( this ).closest(".label_box").find("input[name^=happy_]").prop('value', '1');
$( this ).nextAll("input[name^=happy_").eq(0).prop('value', '1');
});
And an example of the HTML would be something like this:
<div class="image_box">
<img src="sunglasses.jpg">
</div>
<div class="label_box">
<input name="happy_1">
</div>
Essentially, I'm trying to select the input of happy_* in another div directly after finding a matching image.
Never mind, I figured it out! I just needed to add 2 .parent() functions to get up tot he correct div.
$(this).parent().parent().next('div').find("input[name^=happy_]")

jQuery.toggle() not working on a div

On a web page we have a list of profiles. On the right hand side of the profile is some text, followed by an arrow img#arrow.
When img#arrow is clicked, we have the following jQuery we hope to run:
However, the corresponding .bottom-sec is not toggling.
jQuery('#arrow').click(function(){
var $parent = $(this).parent();
$($parent).addClass('active');
jQuery($parent +' .bottom-sec').toggle();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="profile-right">
<h2>Bob Brown</h2>
<h3>Non-Executive Chairman</h3>
<p>Intially showing text.</p>
<div class="bottom-sec" style="display: none;">
<p>Initially hidden text.</p>
</div>
<img id="arrow" src="/wp-content/themes/wtc/images/icons/down-arrow-circle-hi.png">
</div>
Problem
The problem with your code is exactly what the comment on your question is saying, but he didn't explain anything:
You're combining two different ways of selecting elements. One is with selectors, the other is traversing. You're using them in a way which isn't possible (the $parent + ' .bottom-sec' part). The comment linked to a jQuery page about traversing which you should definitely read! It tells you a lot about how to use traversing functions, which you could use!
Solution
There are multiple solutions to this, but I'll write down the one I think is the best:
First of all, change the HTML a bit. I've removed the element style of .bottom-sec and changed the id of the image to a class, because you have multiple images with the same id on the page, which is not a recommended thing to do. Classes can occur more than once, id's cannot.
<div class="profile-right">
<h2>Bob Brown</h2>
<h3>Non-Executive Chairman</h3>
<p>Intially showing text.</p>
<div class="bottom-sec">
<p>Initially hidden text.</p>
</div>
<img class="arrow" src="/wp-content/themes/wtc/images/icons/down-arrow-circle-hi.png">
</div>
I've reduced the JavaScript to the following. Note that is just reduced to one line, where a click on the .arrow element goes searching for the closest .profile-right parent. If, for whatever reason, you decide to change the HTML and the .arrow element is no longer a child of the .profile-right, this code still works. The only thing it does is toggle an active class on the .profile-right.
jQuery(document).on('ready', function() {
jQuery('.arrow').on('click', function(){
jQuery(this).closest('.profile-right').toggleClass('active');
});
});
The document ready listener was added because of OP's comment.
With CSS, we can use the new .active class to show or hide the element.
.profile-right .bottom-sec {
display: none
}
.profile-right.active .bottom-sec {
display: block
}
Original Code Fix
If for some reason you wanted to use your original code, this is how it should be:
// Nothing wrong about this part.
// Your only worry should be that there could be
// multiple elements with the same ID, which is something really bad.
jQuery('#arrow').click(function(){
// This part is correct, no worries
var $parent = $(this).parent();
// Removed the $(...), because $parent is already a jQuery object
$parent.addClass('active');
// Changed the selector to a find function
$parent.find('.bottom-sec').toggle();
});
You could also combine all of the code inside the listener function to just one line:
jQuery('#arrow').click(function(){
$(this).parent().addClass('active').find('.bottom-sec').toggle();
});
Change your js code like below.
jQuery('#arrow').click(function(){
var $parent = $(this).parent();
$($parent).addClass('active');
jQuery($parent).find('.bottom-sec').toggle();
});
In your event listener you can catch the element (the down arrow) that triggered the event. It will be referred as this.
Then you can go through the DOM tree using .next() and .parent() to access the <div> to toggle.
Note: you may need more functions than the one I explained above.
Note 2: without code or more detailed information, we can't help you further, I will edit this answer if you add details.

Adding and removing classes not working

I've used this exact code on a different div element and it works perfectly. When I went to add the same code to another div element with a different id it registers the element has been clicked but it doesn't add or remove any of the classes.
$('#quoteClick').click(function(){
$('#cbox-1').addClass('displayCboxBackground');
$('#cbox-2').removeClass('displayCboxBackground');
$('#cbox-3').removeClass('displayCboxBackground');
$('#dbox-1').addClass('displayBlock');
$('#dbox-2').removeClass('displayBlock');
$('#dbox-3').removeClass('displayBlock');
console.log("clicked");
});
The html structure is as follows:
<div id="cbox-1">
<div id="dbox-1">
content...
</div>
</div>
<div id="cbox-2">
<div id="dbox-2">
content...
</div>
</div>
<div id="cbox-3">
<div id="dbox-3">
<div id="quoteClick">
a quote
</div>
</div>
</div>
JSFiddle: https://jsfiddle.net/m81c23cx/1/
In the fiddle you can see the content will changes when each header is clicked. When the "quoteClick" element is clicked I want it to change to the second headers content exactly how it does when the second header is clicked.
I can see in Chrome's console that when I click the div element that it highlights all the classes but it doesn't change any of them. I have the jQuery inside a document.ready() function so it should be waiting for the DOM to load and it works perfectly when I just write the lines into the console.
I'm surprised that nobody actually questioned your use of ids (instead of suggesting that you should double-check for dupes). The reason why this code is hard to debug is because it's too complicated. As a result, you'll have a hard time fixing issues similar to this in the future too.
Drop it, do it better.
I didn't even go through your fiddle. Instead, I'm going to propose that you change your approach altogether.
Update your HTML and use classes instead of ids. Something similar to this:
<div class="cbox">
<div class="dbox">
content...
</div>
</div>
<div class="cbox">
<div class="dbox">
content...
</div>
</div>
<div class="cbox">
<div class="dbox">
<div id="quoteAdvert">
a quote
</div>
</div>
</div>
Update your JavaScript and use this to get the context of the current box:
$('.cbox').click( function cboxClicked () {
// Remove the previous class from all .cbox & .dbox elements; we don't care which
$('.cbox').removeClass('displayCboxBackground')
$('.dbox').removeClass('displayBlock')
// Add a new class to the clicked .cbox & it's child .dbox
$(this).addClass('displayCboxBackground')
$(this).children('.dbox').addClass('displayBlock')
})
The beauty of this? You can have 1000 boxes, it'll still work. No need to add any extra lines of code.
Here's a fiddle showing it in action.
The example code you provided is not consistent with the jsfiddle you created.
In your fiddle, you use the jquery selector $('#quoteClick') but there is no element with that id. There is a #quoteAdvert element however. Change that and you'll see the click in the console.
The classList property returns a token list of the class attribute of the element in question. Luckily for us, it also comes with a few handy methods:
add - adds a class
remove - removes a class
toggle - toggles a class
contains - checks if a class exists
// adds class "foo" to el
el.classList.add("foo");
// removes class "bar" from el
el.classList.remove("bar");
// toggles the class "foo"
el.classList.toggle("foo");
// outputs "true" to console if el contains "foo", "false" if not
console.log( el.classList.contains("foo") );
// add multiple classes to el
el.classList.add( "foo", "bar" );

Showing and hiding text on more than one place on a website by clicking a single link

I want to show and hide content on a webpage by clicking a link 'Click for More text'. While this works fine, my intention is to display more text in two places on the page at the same time.
How can I 'unhide' and hide two different div id's by one click?
<script type="text/javascript">
  function unhide(divID) {
    var item = document.getElementById(divID);
    if (item) {
      item.className=(item.className=='hidden')?'unhidden':'hidden';
    }
}
</script>
and the HTML:
<a href="javascript:unhide(‘content’);”>Click for More text</a>
<div id=“content” class="hidden">
hi
</div>
<div id=“content2” class="hidden">
how can i display this from the same link..?
</div>
Put them in one more div to wrap it and then show just that one
<a href="javascript:unhide(‘content_wrapper’);”>Click for More text</a>
<div id="content_wrapper" class="hidden">
<div id=“content”>
hi
</div>
<div id=“content2”>
how can i display this from the same link..?
</div>
</div>
If you are using jQuery, better idea would be to use classes, check the code below for example
HTML:
<button onclick="unhide('more_info')">
Click for More text
</button>
<div class="more_info hidden">
hi
</div>
<div class="more_info hidden">
how can i display this from the same link..?
</div>
Javascript:
function unhide (arg) {
// toggle class, or remove or add, what ever you need
$('.'+ arg).toggleClass('hidden');
}
EDIT:
To answer question posted by OP in comments.
When it comes to jQuery, most people use only couple of forms of selectors. You can visit this link to find out more about selectors.
For the basics, you are mostly going to be using 2 forms. Personally I use class selector in most cases which is '.selector'
What you can do with it means you use it in form of $('.classSelector') where classSelector can be any class you want to select.
Couple of examples
<div id="test-div-id" class="test-div-class">
<p class="paragraph paragraph-1">This is first</p>
<p class="paragraph paragraph-2">This is second</p>
<p class="paragraph paragraph-3">This is third</p>
</div>
For javascript, you can then use following
$('.test-div-class')
// returns the div by selecting it's class
$('#test-div-id')
// returns the div by selecting it's ID
So if you wanted to check the value of first paragraph you could do
$('.paragraph-1').html();
// returns 'This is first'
You can also select multiple things, let's say you want to hide all paragraphs, you could use .hide() function from jQuery.
$('.paragraph').hide();
// the selector returns collection of all nodes containing class 'paragraph'
// after that we apply function hide.
The last one works on all classes, so you could mix paragraphs and divs and spans and what not. That brings us to next selector, by type
$('p').hide();
// this selector will return every paragraph by type selection
And you can also use what I did in the answer, simple adding of strings
$('.paragraph-1').html();
// returns 'This is first'
var selectorAsAnVariable = 'paragraph-1';
$(selectorAsAnVariable).html();
// returns nothing since it didn't select anything
// this is same as writing $('paragraph-1').html() which would be type selection
// since you don't have type paragraph-1 it fails
$(.selectorAsAnVariable).html();
// this fails on syntax error because unexpected token
$('.selectorAsAnVariable').html();
// returns nothing since it didn't select anything
// this is because you would be trying to select elements which really have that class
$('.'+selectorAsAnVariable).html();
// returns 'This is first'
// this is because this is same as $('.'+'paragraph-1').html()
// which is same as $('.paragraph-1').html() which we know is an class selector
You can also mix them, but I would advise against it because of performance issues, code readability and other reasons, for example you can target div by class and filter paragraph-1 from there. But in most cases it is better to write your code in way that you can avoid that.
For more about the topic, check the link I provided. Also you can use the search to look for other function explanations there.
I hope this clarified things a bit :)

How to temporarily block selectable elements

I am working on a popup menu on my webpage. The menu contains various selectable items, and I would like to only allow selection of certain items after a top-selection has been made. Now I could hide all items lower-down, but that would make the popup look weird. I'd rather show them, but dimmed. My idea was to enclose the follow-up selections in a div, and have that div act as a blocker. Now the question is how to do it - I tried setting the z-index of the selBlocker div higher than the rest, also to give it absolute positioning, but didn't get anywhere yet. I am using a javascript library to handle the selections in general.
<div id="SelPopup" >
<div id="topSelect"></div>
<div id="selBlocker">
<div id="selectable2"></div>
<div id="selectable3"></div>
</div>
</div>
I would append a class to the items you dont want to select, and add the not() selector to your jQuery.
For example:
$("div:not('.selected').....
Instead of
$("div").....
Ofcourse you can add an opacity to the class .selected, to make it a little bit less visible.
You can try below:
Instead of using id for selection blocked element use class="selectBlocked" and for menu div use class="selectMenu"
<div id="SelPopup" >
<div id="topSelect" class="selectMenu"></div>
<div id="selBlocker1" class="selectMenu selectBlocked">
<div id="selectable2"></div>
<div id="selectable3"></div>
</div>
<div id="selBlocker4" class="selectMenu selectBlocked">
<div id="selectable5"></div>
<div id="selectable6"></div>
</div>
</div>
Now right jQuery for handling selection of menu and do nothing if selected menu is with class="selectBlocked"
$('.selectMenu').click(function(){
if($(this).hasClass("selectBlocked"))
return false;
// do your stuff if above condition fails
});
Thank you all for the suggestions, I actually found what I was looking for:
$("#selBlocker").css("pointer-events", "none");
This will nicely disable all interaction, and with
$("#selBlocker").css("pointer-events", "all");
I can restore it. Can add the change in opacity easily alongside it.

Categories

Resources