bootstrap carousel hide controls on first and last - javascript

How can I hide the left control if the carousel is on the first item, and how can I hide the right control when the carousel is on the last item.
My code below hides the control successfully but on page load it is as if the carousel first item is in the middle and the user can either go all the way through via the left or right controls.
http://bootply.com/99354
thanks

Bootply link
$('#myCarousel').on('slid', '', checkitem); // on caroussel move
$('#myCarousel').on('slid.bs.carousel', '', checkitem); // on carousel move
$(document).ready(function(){ // on document ready
checkitem();
});
function checkitem() // check function
{
var $this = $('#myCarousel');
if($('.carousel-inner .item:first').hasClass('active')) {
$this.children('.left.carousel-control').hide();
$this.children('.right.carousel-control').show();
} else if($('.carousel-inner .item:last').hasClass('active')) {
$this.children('.left.carousel-control').show();
$this.children('.right.carousel-control').hide();
} else {
$this.children('.carousel-control').show();
}
}

The below code is an updated version of TheLittlePig's code for Bootstrap 3 that works both for multiple carousels on the same page and for indicator actions. The explained code is here
checkitem = function() {
var $this;
$this = $("#slideshow");
if ($("#slideshow .carousel-inner .item:first").hasClass("active")) {
$this.children(".left").hide();
$this.children(".right").show();
} else if ($("#slideshow .carousel-inner .item:last").hasClass("active")) {
$this.children(".right").hide();
$this.children(".left").show();
} else {
$this.children(".carousel-control").show();
}
};
checkitem();
$("#slideshow").on("slid.bs.carousel", "", checkitem);

Augmenting #TheLittlePig, it needs to be slightly different if you're using Bootstrap 3 because the event to attach the callback to is different: slid.bs.carousel. Also, if you have multiple carousels on one page you'll need to pass a unique css id for the carousel into the event handler. Here is a modified version that I use on my Rails site:
<script>
//<![CDATA]
id = '#carousel-<%=id%>';
$(id).on('slid.bs.carousel', { id: id }, bs_carousel_slid);
$(document).ready(function(){ $(id).trigger('slid.bs.carousel'); });
//]]>
</script>
That is repeated for each carousel. The <%=id%> is a ruby expression that is replaced by a unique id for the given carousel. Tweak that bit for your needs according to the language of your choice.
The difference is that the carousel's id is passed into the event handler function as event data so that the event handler can operate on the correct carousel. I also changed the ready event so that it triggers the slid.bs.carousel event (instead of calling the function directly) so it passes the correct event data to the event handler for each carousel.
The event handler is a function called bs_carousel_slid that I define elsewhere (those on Rails - it's in a file in app/assets/javascripts). The function is shown below:
function bs_carousel_slid(event)
{
var id = event.data.id;
var $this = $(id);
if($(id + ' .carousel-inner .item:first').hasClass('active')) {
$this.children('.left.carousel-control').hide();
} else if($(id + ' .carousel-inner .item:last').hasClass('active')) {
$this.children('.right.carousel-control').hide();
} else {
$this.children('.carousel-control').show();
}
}

IF YOU'RE USING BOOTSTRAP 3:
The event is 'slid.bs.carousel' not 'slid'
$('.carousel').carousel({
interval: false,
})
$(document).ready(function () { // on document ready
checkitem();
});
$('#myCarousel').on('slid.bs.carousel', checkitem);
function checkitem() // check function
{
var $this = $('#myCarousel');
if ($('.carousel-inner .item:first').hasClass('active')) {
$this.children('.left.carousel-control').hide();
} else if ($('.carousel-inner .item:last').hasClass('active')) {
$this.children('.right.carousel-control').hide();
} else {
$this.children('.carousel-control').show();
}
}

Related

Why Doesn't Modal FadeOut Slowly?

I inherited this modal/overlay/content close/empty method that works, but abruptly:
method.close = function () {
$modal.hide();
$overlay.hide();
$content.empty();
$(window).unbind('resize.modal');
};
To fade out gradually, I modified the method like below, but elements are left behind and subsequent clicks don't open new modals loaded with content, only the overlay:
method.close = function () {
$modal.fadeOut('slow', function() {
$(this).hide();
});
$overlay.fadeOut('slow', function() {
$(this).hide();
});
$content.fadeOut('slow', function() {
$(this).empty();
});
$(window).unbind('resize.modal');
};
What am I missing?
UPDATE: The solution is a single nested callback, based on garryp's answer, like this:
method.close = function() {
$overlay.fadeOut('slow', function() {
$overlay.hide();
$content.empty();
});
$modal.hide();
$(window).unbind('resize.modal');
};
Hide is asynchronous; the calls you have in your original code do not block while the transition occurs, execution moves immediately to the next. You need to use callbacks, like this:
var me = $(this); //Added to ensure correct this context
$modal.fadeOut('slow', function () {
me.hide(function () {
$overlay.fadeOut('slow', function () {
me.hide(function () {
$content.fadeOut('slow', function () {
me.empty();
});
});
});
});
});
Assuming the rest of your code is correct this should ensure the transitions fire one after the next.
Firstly, you do not need $(this).hide(). JQuery fadeOut automatically set display: none at the end of fading animation (read more: http://api.jquery.com/fadeout/).
That mean, in your case $content element will also have display: none after fadeOut animation. I expect you forgot to add $content.show() in modal open method.

How to manage queuing of a Jquery toggle fade animation?

QUESTION
How can I prevent a jquery toggle function from running before the previous toggle animation is complete?
I have a simple script to show or hide data depending whether a checkbox is checked.
JQUERY
$('.a').hide();
$('#CB').change(function () {
if ($(this).is(':checked')) {
$('.b').fadeOut(100, function () {
$('.a').fadeIn();
});
} else {
$('.a').fadeOut(100, function () {
$('.b').fadeIn();
});
}
});
PROBLEM
When the event is fired consecutively both elements, in this case .a and .b become visible together. I assume this is because the previous request is not completed prior to firing the function again.
CLICK FOR DEMO
http://jsfiddle.net/keypaul/PbS33/5/
$('.a').hide();
$('#CB').change(function () {
if ($(this).is(":checked")) {
$('.b').stop().fadeOut(100, function () {
$('.a').stop().fadeIn();
});
} else {
$('.a').stop().fadeOut(100, function () {
$('.b').stop().fadeIn();
});
}
});
Using jquery stop()
http://api.jquery.com/stop/
You're right. Animations in jQuery work asynchronously so they could sometimes run at the same time.
To answer your question, I think you already answered it in your question title.
Use a queue.
Set up a flag, name it something like isFading, and when it's true when $("#CB") changes, you queue it instead.
var isFading=false;
var animationQueue = [];
$('#CB').change(function () {
if(isFading){
if ($(this).is(':checked')) {
animationQueue.push(fadeOutFadeIn);
}
else {
animationQueue.push(fadeInFadeOut);
}
}
else{
if ($(this).is(':checked')) {
fadeOutFadeIn();
} else {
fadeInFadeOut();
}
}
);
function fadeOutFadeIn(){
isFading=true;
//insert your fadeout fadein code here
isFading=false;
if(animationQueue.length > 0)
//you're now calling the queued animation to go through
animationQueue.splice(0,1)();
}
function fadeInFadeOut(){
isFading=true;
//insert your fadein fadeout code here
isFading=false;
if(animationQueue.length > 0)
//you're now calling the queued animation to go through
animationQueue.splice(0,1)();
}

stop event after first time

i am firing an event when im at a special scrollposition with jquery.inview. it works by adding classes if an element is in the viewport. in my script im saying the following
var $BG = $('#BG')
$('#BG').bind('inview', function (event, visible)
{
if (visible == true) {
$(this).addClass("inview");
} else {
$(this).removeClass("inview");
}
});
if($BG.hasClass("inview")){
$('#diagonal').css('left',0)
$('#diagonal').css('top',0)
}
but it fires the .css events again and again, but i want them to fire only at the first time the #BG gets the "inview" class.
thanks ted
You can add some var who tells if it has been fired or not :
var $BG = $('#BG'), firedInView = false;
$BG.bind('inview', function (event, visible) {
if(!firedInView) {
firedInView = true; //set to true and it won't be fired
//do your stuff
}
});
You can unbind the event handler using jQuery unbind method or use one method to handle event at most once.
http://api.jquery.com/category/events/event-handler-attachment/
Try with .one() instead .bind():
$('#BG').one('inview',
I am going on the assumption that you would like to remove the styles on diagonal when #BG is out of view?
I'd split this into two listeners
//If bg does not have class inview, addClass if it is visible
$('body').on('inview', '#BG:not(.inview)', function (event, visible) {
if (visible == true) {
$(this).addClass("inview");
$('#diagonal').css({'left': 0, 'top': 0});
}
});
//If bg does has class inview, removeClass if it is invisible
$('body').on('inview', '#BG.inview', function (event, visible) {
if (visible == false) {
$(this).removeClass("inview");
$('#diagonal').css({'left': 'auto', 'top': 'auto'});
}
});

Delay animation until other animation is complete (sliding content(

I have this code which animates between divs sliding out. If an item is clicked, it's relevant content slides out. If another item is clicked, the current content slides back in and the new content slides out.
However,
var lastClicked = null;
var animateClasses = ['ale', 'bramling', 'bullet', 'miami-weisse'];
for (var i=0; i<animateClasses.length; i++) {
(function(animCls) {
$('.each-brew.'+animCls).toggle(function() {
if (lastClicked && lastClicked != this) {
// animate it back
$(lastClicked).trigger('click');
}
lastClicked = this;
$('.each-brew-content.'+animCls).show().animate({ left: '0' }, 1000).css('position','inherit');
}, function() {
$('.each-brew-content.'+animCls)
.animate({ left: '-33.3333%' }, 1000, function() { $(this).hide()}) // hide the element in the animation on-complete callback
.css('position','relative');
});
})(animateClasses[i]); // self calling anonymous function
}
However, the content sliding out once the already open content slides back is sliding out too quickly - it needs to wait until the content has fully slided back in before it slides out. Is this possible?
Here's a link to what I'm currently working on to get an idea (http://goo.gl/s8Tl6).
Cheers in advance,
R
Here's my take on it as a drop-in replacement with no markup changes. You want one of three things to happen when a menu item is clicked:
if the clicked item is currently showing, hide it
if something else is showing, hide it, then show the current item's content
if nothing is showing, show the current item's content
var lastClicked = null;
// here lastClicked points to the currently visible content
var animateClasses = ['ale', 'bramling', 'bullet', 'miami-weisse'];
for (var i=0; i<animateClasses.length; i++) {
(function(animCls) {
$('.each-brew.'+animCls).click(function(event){
if(lastClicked && lastClicked == animCls){
// if the lastClicked is `this` then just hide the content
$('.each-brew-content.'+animCls).animate(
{ left: '-33.3333%' }, 1000,
function() {
$(this).hide();
}).css('position','relative');
lastClicked = null;
}else{
if(lastClicked){
// if something else is lastClicked, hide it,
//then trigger a click on the new target
$('.each-brew-content.'+lastClicked).animate(
{ left: '-33.3333%' }, 1000,
function() {
$(this).hide();
$(event.target).trigger('click');
}).css('position','relative');
lastClicked = null;
}else{
// if there is no currently visible div,
// show our content
$('.each-brew-content.'+animCls).show()
.animate({ left: '0' }, 1000)
.css('position','relative');
lastClicked = animCls;
}
}
});
})(animateClasses[i]); // self calling anonymous function
}
Well, I'm pretty sure there are other more easy possibilities and I didn't have much time but here is a working jsfiddle: http://jsfiddle.net/uaNKz/
Basicly you use the callback function to wait until the animation is complete. In this special case it's the complete: function(){...}
$("document").ready(function(){
$('#ale').click(function(){
if ($('div').hasClass('toggled')){
$('.toggled').animate({ width: "toggle" }, { duration:250, complete: function(){
$('#alecont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');}
}).removeClass('toggled');
}else{
$('#alecont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');
}
});
$('#bramling').click(function(){
if ($('div').hasClass('toggled')){
$('.toggled').animate({ width: "toggle" }, { duration:250, complete: function(){
$('#bramcont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');}
}).removeClass('toggled');
}else{
$('#bramcont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');
}
});
});
I give a toggled class if a div is expanded. Since the animation on your page seems to be pretty much broken I think this would be a better way to do this. But remember: my code isn't really good. Just fast and it can be refactored. It's working tho..
Rather than using toggles, bind an on "click" handler to your ".each-brew" divs. In the handler, first hide content divs and then show the appropriate content when that animation completes. You can do that with either a promise or a callback. Something like this...
$(".each-brew").on("click", function (event) {
$(".each-brew-content").show().animate({ left: "0" }, 1000, function() {
// Get the brew name from the class list.
// This assumes that the brew is the second class in the list, as in your markup.
var brew = event.currentTarget.className.split(/\s+/)[1];
$(".each-brew-content." + brew).animate({ left: "-33.3333%" }, 1000, function() { $(this).hide(); });
});
});
I think an event and observer would do the trick for you.
set up the callback function on completion of your animation to fire an event.
the listener would first listen for any animation event and after that event is triggered listen for the completion event. when the completion event is fired execute the initial animation event.run method (or whatever you would want to call it)
Within the listener
on newanimationeventtriger(new_anim) wait for x seconds (to eliminate infinite loop poss) while if this lastevent triggers done == true{
new_anim.run();
}

Best Practice Combining Toggle and Hover

I have some elements which have hover effects as well as should be selected when clicked. Currently when I add the stop() to the effect it causes the animation to stop in place when clicked. I tried using fadeToggle() for the same effect but could not wrap my head around how to get it to function properly. The id I am targeting are passed to the href of the clicked element.
Can someone give pointers on the best way to write this script?
$(function() {
$("#map-hovers > ul > li").hide();
$('area').click(function(e) {
e.preventDefault();
var $hoodClick = $($(this).attr('href'));
if ($hoodClick.hasClass('selected')) {
$($hoodClick).fadeOut().removeClass('selected');
} else {
$($hoodClick).fadeIn().addClass('selected');
}
}).hover(function() {
var $hoodHoverOver = $($(this).attr('href'));
$hoodHoverOver.fadeIn();
},
function() {
var $hoodHoverOut = $($(this).attr('href'));
if ($hoodHoverOut.hasClass('selected')) {
} else {
$hoodHoverOut.fadeOut();
}
})
});
Use .stop(true, true). It skips to the end of the animation & clears the animation queue. Read more about it in the API: http://api.jquery.com/stop/

Categories