I'm having a problem with jQuery .not() not working.
I've got a pricing table with 3 products, when you mouse over each of the products a description appears and then disappears on mouse out so far everything works fine.
The problem however is that on page load one of the three products (each held in an <aside>) will have a .selected class applied to it and I need the mouseenter/leave function to not run on this <aside>.
The only caveat is that clicking any of the <button>'s in each <aside> will remove the .selected class from it's current <aside> and apply to the <button>'s <aside> parent (I've not included the JQ for this as it's just a dead simple .addClass).
Code example of what I currently have is below, thanks in advanced.
jQuery
$(".pricing3 aside").not('.selected').mouseenter(function() {
$(".desc", this).stop().slideDown('slow');
})
.mouseleave(function() {
$(".desc", this).stop().slideUp('slow');
});
HTML
<div class="pricing3">
<aside>
<i class="fa fa-clock-o"></i>
<h3>1 Hour Pass</h3>
<p class="desc">Description</p>
<p class="pricing">£XX</p>
<button type="submit">BUY NOW</button>
</aside>
<aside class="selected">
<i class="fa fa-clock-o"></i>
<h3>8 Hour Pass</h3>
<p class="desc">Description</p>
<p class="pricing">£XX</p>
<button type="submit">BUY NOW</button>
</aside>
<aside>
<i class="fa fa-clock-o"></i>
<h3>24-7-365 Access</h3>
<p class="desc">Description</p>
<p class="pricing">£XX</p>
<button type="submit">BUY NOW</button>
</aside>
</div>
Need to use event delegation as the target element selection is based on a dynamic state
$(".pricing3").on('mouseenter', 'aside:not(.selected)', function () {
$(".desc", this).stop().slideDown('slow');
}).on('mouseleave', 'aside:not(.selected)', function () {
$(".desc", this).stop().slideUp('slow');
});
It can be written slightly differently as
$(".pricing3").on({
mouseenter: function () {
$(".desc", this).stop().slideDown('slow');
},
mouseleave: function () {
$(".desc", this).stop().slideUp('slow');
}
}, 'aside:not(.selected)')
As the class is shifting you'd be better off checking for the class inside the event handler
$(".pricing3 aside").on('mouseenter mouseleave', function() {
if ( !$(this).hasClass('selected') ) {
$(".desc", this).stop().slideToggle('slow');
}
});
If I understand you correctly, the problem is that you want the .selected to be removed, and then the mouseenter() to run on it as if it hadn't had the .selected on it in the first place?
If this is the case, you should attach your event handler higher up the DOM tree, and use on() to filter down to the <aside>s This way you can flip/flop the class all you want, and the event handler will work as expected. As it's coded, you are only applying the filter at page load, so the state change of the class has no effect.
Related
I am using Bootstrap 5.2 and I have two buttons that can hide content, using the Bootstrap collapse plugin.
<div class="col-12 col-sm-auto">
<span class="pe-2">
<button id="show_movements_button" type="btn" class="btn btn-outline-primary" data-bs-toggle="collapse" data-bs-target="#movements_id">
Show Movements
</button>
</span>
<span class="pe-2">
<button id="show_credits_button" type="btn" class="btn btn-outline-secondary" data-bs-toggle="collapse" data-bs-target="#credits_id">
Show All Credits
</button>
</span>
</div>
such as
<tr class="song_id collapse" id="movements_id">
<td class="col-1">
1
</td>
<td class="col">
</td>
<td class="col">
<div>
<label class="h6">
Piano Concerto no. 1 in E minor, op. 11: I. Allegro maestoso
</label>
</div>
<div class="collapse" id="credits_id">
<div class="lh-1">
<div>
<a href="/container.start?cid=0$=Instrument$708&title=Instruments+%2F+piano" class="small text-secondary">
piano
</a>
<label class="small">
by
</label>
<a href="/container.start?cid=0$=Performer_name$5540&title=Performers+%2F+Evgeny+Kissin" class="small text-secondary pe-1">
Evgeny Kissin
</a>
</div>
</div>
</div>
</td>
</tr>
This work correctly, but I want the name of the button to change to indicate if showing or hiding content so I also have this code
<script>
function listenForButtonCollapse(buttonId, collapseId, buttonShowText, buttonHideText)
{
let button = document.getElementById(buttonId);
let section = document.getElementById(collapseId);
if(section!=null)
{
section.addEventListener('show.bs.collapse',
function()
{
button.innerText=buttonHideText;
}
);
section.addEventListener('hide.bs.collapse',
function()
{
button.innerText=buttonShowText;
}
);
}
}
</script>
<script>
listenForButtonCollapse('show_credits_button','credits_id','Show All Credits','Hide Some Credits');
</script>
<script>
listenForButtonCollapse('show_movements_button','movements_id','Show Movements','Hide Movements');
</script>
Now toggling the Show/Hide Movements button works fine, but when I click on the Show/Hide Credits button for some reason it is also triggering the listenForButtonCollapse() call on the movements button as well as the credits button, so the Movement button is updated with the same (Hide/Show) value as the credits button even though it isn't actually been invoked (so it doesn't hide/show the movements div)
The credits div is within the movements div, so Im assuming that why one button works without problem and the other doesn't but I cant see what I am actually doing wrong.
In order to avoid the current behaviour you need to stop further event propagation. You can do this by using stopPropagation(). This method of the Event interface prevents further propagation of the current event in the capturing and bubbling phases.
function listenForButtonCollapse(buttonId, collapseId, buttonShowText, buttonHideText)
{
let button = document.getElementById(buttonId);
let section = document.getElementById(collapseId);
if (section != null)
{
section.addEventListener('show.bs.collapse',
function(event)
{
event.stopPropagation();
button.innerText=buttonHideText;
}
);
section.addEventListener('hide.bs.collapse',
function(event)
{
event.stopPropagation();
button.innerText=buttonShowText;
}
);
}
}
I've prepared a Code playground to illustrate that this solves your issue:
https://codesandbox.io/embed/bootstrap-5-playground-forked-b28j0g?fontsize=14&hidenavigation=1&theme=dark
Solution Explanation
The events show.bs.collapse and hide.bs.collapse are being triggered for both buttons when clicked. So when you add more than one event listener for those events, when the event occurs, all event listeners are being executed. This is why for example when you click "Hide Movements" and bootstrap triggers hide.bs.collapse
event then this executes all registered event listeners for it - in this case two - one for the show_credits_button and another for show_movements_button button - leading to changing both button texts. To prevent this you need to stop further event propagation with event.stopPropagation() method - this will stop notifying all other event listeners for this event other than the event target one.
I have some buttons and I added some behavior on click to all of them with jQuery and they worked fine, but now that those buttons are generated dynamically by changing the innerHTML of the div with a script, the behavior don't work anymore
Here is an example, like this every time I click any of the two buttons, it show an alert with the message 'clicked'.
Fiddle
$('.foo').on('click', function(){
alert('clicked');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="home">
<button class='foo' > Test </button>
<button class='foo' > Test </button>
</div>
But if I insert the buttons by changing the innerHTML of home with the button generate, it does not works anymore
Fiddle
$(".gen").on("click", function(){
$('.home').html(
"<button class='foo' > Test </button>" +
"<button class='foo' > Test </button>");
})
$('.foo').on('click', function(){
alert('clicked');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<button class="gen" > Generate </button>
</div>
<div class="home">
</div>
I really don't know what's going on
$('.foo') selects certain elements. .on() adds an event handler to only the selected elements. Any new elements with the "foo" class will not have that handler. Either add them manually to the new elements or better still use a delegate.
Basically, since your "foo" elements do not exist until after you click "generate", the call to .on() adds a handler to nothing.
Here's a solution using jQuery's delegate implementation
$(".gen").on("click", function(){
$('.home').html(
"<button class='foo' > Test </button>" +
"<button class='foo' > Test </button>");
})
$(document).on('click', '.foo', function(){
console.log('clicked');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<button class="gen" > Generate </button>
</div>
<div class="home">
</div>
This is probably a known issue or question. However, I only see results for
Element.on('click', callback)
events and I do not know how to do this with and on 'mouseout' event
This is my HTML:
<div class="oneTodo">
<div class="todoText"></div>
<span class="eraseTodo">
<i class="icon ion-ios-close-outline"></i>
</span>
</div>
And my script:
$('.oneTodo').hover(function () {
$(this).find('.eraseTodo').fadeIn(100);
});
$('.oneTodo').on('mouseout', function() { // this is what is not working
$(this).find('.eraseTodo').fadeOut(100);
});
Basically when I hover over my:
<span class="eraseTodo">
<i class="icon ion-ios-close-outline"></i>
</span>
since it basically is not the .oneTodo div anymore it fades Out. I want to prevent this. How can I achieve this?
P.S: I tried:
$('.oneTodo:not(.eraseTodo)').on('mouseout', function() {
$(this).find('.eraseTodo').fadeOut(100);
});
but failed miserably.
I'm fairly new to Javascript/Jquery and I'm trying to hide multiple children/adjacent classes when a specific parent class is clicked.
Here's my HTML
<div class="container">
<div class="row">
<div class ="col-md-2 pov_icon">
<div class="pov_icon_small">
<i class="fa fa-clock-o"></i>
</div>
<div class="pov_title_small">
MEASURE
</div>
</div>
<div class ="col-md-2 pov_icon">
<div class="pov_icon_large">
<i class="fa fa-map-marker"></i>
</div>
<div class="pov_title_large">
MEASURE
</div>
</div>
<div class ="col-md-2 pov_icon">
<div class="pov_icon_small">
<i class="fa fa-commenting"></i>
</div>
<div class="pov_title_small">
MEASURE
</div>
</div>
</div>
</div>
What I'm aiming to do is: When a user clicks one of the two smaller icons shown (pov_icon_small), for that individual icon: the classes pov_icon_small and pov_title_small will change to pov_icon_large and pov_title_large respectively. In the same time, I want the other 'large' icon and 'title' to revert back to the 'small' state
I've started calling some Javascript but I don't think I'm headed the right way:
$('.pov_icon_small').on('click', function (e) {
$(this).toggleClass("pov_icon_large");
});
Would anyone be willing to point me to the right direction?
To use individual click
$('.pov_icon_small , .pov_icon_large').on('click', function (e) {
$('.pov_icon_large').not($(this)).removeClass('pov_icon_large').addClass('pov_icon_small');
$(this).toggleClass("pov_icon_small").toggleClass("pov_icon_large");
});
and for title the same way
$('.pov_title_small , .pov_title_large').on('click', function (e) {
$('.pov_title_large').not($(this)).removeClass('pov_title_large').addClass('pov_title_small');
$(this).toggleClass("pov_title_small").toggleClass("pov_title_large");
});
Working Demo
To run both action on icon click use this
$('.pov_icon_small , .pov_icon_large').on('click', function () {
$('.pov_icon_large').not($(this)).removeClass('pov_icon_large').addClass('pov_icon_small');
$('.pov_title_large').not($(this).next('div[class^="pov_title_"]')).removeClass('pov_title_large').addClass('pov_title_small');
$(this).toggleClass("pov_icon_small").toggleClass("pov_icon_large");
$(this).next('div[class^="pov_title_"]').toggleClass("pov_title_small").toggleClass("pov_title_large");
});
Working Demo
Note: be sure to include Jquery
You can add a common class icon for the icon div and title for the title div and following code will work,
$(".pov_icon_small").on('click',function(){
$(this).parent().siblings().children('div').each(function(value){
if($(this).hasClass('icon'))
$(this).addClass('pov_icon_small').removeClass('pov_icon_large');
else if($(this).hasClass('title'))
$(this).addClass('pov_title_small').removeClass('pov_title_large');
});
$(this).addClass('pov_icon_large').removeClass('pov_icon_small');
$(this).siblings('.title').addClass('pov_title_large').removeClass('pov_title_small');
});
Here as you can see, I am first getting parent of the icon clicked i.e. Your pav_icon div now I am changing for all the siblings now each div in the sibling. If it is Iicon changing icon classes as required if title changing title classes.
I have the following html:
<div class="guide-block" id="energy_guide">
<div class="guide-block-inner">
<div class="guide-block-head">
<a class="guide-block-link" href="#">
<h3 class="guide-block-title">Guides</h3>
</a>
</div>
<div class="guide-block-image">
<div class="guide-block-image-inner" id="energy_guide">
<img src="images/Energy-Saving-Bulb-01.png" alt="Guide">
</div>
</div>
<div class="guide-block-more">
<ul class="guide-block-list us-list" id="energy_guide">
<li>News</li>
<li>Guides</li>
</ul>
</div>
</div>
</div>
and the following js:
$( "#energy_guide" ).mouseenter(function()
{
$(".guide-block-image #energy_guide")
.fadeOut("fast", function()
{
$(".featured_news .guide-block-inner .guide-block-more #energy_guide")
.fadeIn("fast");
});
});
$( "#energy_guide" ).mouseleave(function()
{
$(".featured_news .guide-block-inner .guide-block-more #energy_guide")
.fadeOut("fast", function ()
{
$(".guide-block-image #energy_guide")
.fadeIn("fast");
});
});
The list is hidden by default in the css and what i want to achieve is to replace the image with the list while hovering the mouse over the whole guide-block div. Everything is fine and dandy until you move your mouse too fast, the mouseleave function not being triggered for some reason.
Just a first idea that hit me was that actually your events are totally fine, rather the way jQuery constructs Animations and builds them in the queue.
a probable fix would be to .stop() your subsequent animations (anim. buildups) so an overall code could look like:
$( "#energy_guide" ).hover(function() {
$(this).stop().fadeToggle();
});
(Without seeing a demo of your code with CSS it's a bit hard to guess but here you go)
The way you use your selectors is wrong:
.featured_news .guide-block-inner .guide-block-more #energy_guide
jQuery will select only #energy_guide and taking in considerations that it's the only ID in your document (as it should be) there's no need to use parent Class selectors.