Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

If content is hidden during Slick bootstrap, Slick refuses to play ball #187

Closed
alexrussell opened this issue Apr 28, 2014 · 63 comments
Closed

Comments

@alexrussell
Copy link

I'm not sure that this is easily fixable but I'm implementing Slick on a Bootstrap website and I have a Slick-based gallery in a set of Bootstrap tabs. If the gallery tab is the active one on page load, the Slick carousel works fine, but if the gallery tab is not the one that's active on page load the Slick carousel seems to break (the tab basically looks empty).

At a guess this is due to the items being hidden as Slick is set up against the items and so Slick can't calculate the dimensions of the slides, etc.

A possible non-Slick-based fix is to hook into BS tab event system and only call $.fx.slick() when the tab is first switched to. Of course this only works for JavaScript components that allow a developer to hook into this kind of event system, so maybe there's a way for Slick to get round this limitation without requiring developers to do these hackish bits of code (which they sometimes may not be able to do).

@kenwheeler
Copy link
Owner

Sounds like because it is "display: none;" that it can't get the dimensions. I would try manually firing a window resize event on tab change, and see if that gives it a kick in the ass.

@alexrussell
Copy link
Author

Looking into the source, there are kinda a few places where widths are calculated and passed around, but it all seems to boil down to the slick-list element that is injected: on line 1143 its width is calculated and used in a further calculation of the width of a slide. From those values of listWidth and slideWidth all other widths are calculated. Maybe something can be done at this point to make the element 'positioned' and thus able to have its width calculated.

@alexrussell
Copy link
Author

@kenwheeler the resize thing seems to fix it. As I said in my original issue text this does rely on a developer's ability ot actually hook into these events, so it'd still be good to see if Slick could do its own workaround.

The code I used in the end was:

// set up Slick carousel
$('.gallery').has('img + img').slick({
    autoplaySpeed: 5e3,
    arrows: false,
    infinite: true,
    slide: 'img',
    slidesToScroll: 1,
    slidesToShow: 1,
    speed: 300,
    touchMove: false,
});

// hook into Bootstrap shown event and manually trigger 'resize' event so that Slick recalculates the widths
$('[href="#gallery"]').on('shown.bs.tab', function (e) {
    $('.gallery').resize();
});

@kenwheeler
Copy link
Owner

You can manually refresh slick itself using slickSetOption(anyoption, anyvalue, true);

@alexrussell
Copy link
Author

Still doesn't really fix the underlying problem, but fair enough if you think this isn't something that can really be done by slick itself.

@kenwheeler
Copy link
Owner

Any suggestions on how?

@kenwheeler
Copy link
Owner

The underlying problem is essentially a javascript/dom problem, that you can't get the dimensions of hidden elements.

@alexrussell
Copy link
Author

Well I know that, for example, various JS libraries (Prototype and jQuery) do try their best to make this happen by quickly making an element displayed (i.e. not display: none) but invisible (i.e. opacity: 0 or visibility: hidden) to temporarily calculate the width/height of things, before them resetting this. However, I think the main issue is that it could be that a parent of the element in question that is not displayed rather than the element itself, that required more work to do. It's still technically possible, but yeah maybe it's too much to ask of a widget like slick versus a library like jQuery, etc. (I wonder if you can somehow subscribe to a 'visibility changed' event on an element. I haven't heard of such things, but that'd be a perfect fix.)

I guess the dependency on jQuery to do all of this DOM stuff suggests it's more an upstream issue than a slick issue. The problem is that jQuery doesn't seem to be able to return the width of an element whose parent is set to display: none.

Still, it may be worth mentioning this as a caveat somewhere in the docs - just mention that obviously if the element is hidden when you call .slick() on it, slick will incorrectly calculate its sizes, and slick must be refreshed when the element is finally displayed. Or if slick detects the width as 0, it puts a message in the console suggesting the developer do something about this. I dunno.

@kenwheeler
Copy link
Owner

It's not a jQuery issue, its a browser/DOM issue. You can't get the dimensions of a hidden element. As the implementor, if you are hiding slick, be sure to hide it after it has initialized, or mitigate the dimension calculation by triggering a resize event.

@richard-keller
Copy link

I think it might be worth putting this somewhere in the docs. I've also just run into this problem while using Bootstrap 3 tabs (when the carousel is placed in a tab that is initially hidden), and it wasn't immediately clear how to fix the issue. As already mentioned, calling resize() fixes the problem.

@kenwheeler
Copy link
Owner

Yeah I think it's high time I write up a FAQ section to clear up certain things that implementors may encounter,
But that don't merit a fix/inclusion to the core library itself.

@patrickvaillancourt
Copy link

The .resize() option didn't work for me. I experienced a similar problem attempting to render an embedded google map inside a bootstrap tab as well. To my surprise both problems were resolved by changing how bootstrap handles hidden tabs:

/* bootstrap hack: fix content width inside hidden tabs */
.tab-content > .tab-pane, .pill-content > .pill-pane {
    display: block;    /* undo display:none          */
    height: 0;         /* height:0 is also invisible */ 
    overflow: hidden;  /* no-overflow                */
}
.tab-content > .active, .pill-content > .active {
    height: auto;      /* let the content decide it  */
} /* bootstrap hack end */

more here: https://groups.google.com/forum/#!topic/twitter-bootstrap-stackoverflow/0Aqzs5s_hEY

@JensNielsen81
Copy link

This was also driving me crazy...
I´m using jquery UI tabs, where sliders in inactive tabs were not being formatet correctly.
I fixed it with a little help from @patrickvaillancourt css solution.

This is what i did:

.ui-widget-content[aria-hidden="true"] {
    display: block !important;
    height: 0px;
    overflow: hidden;
}
.ui-widget-content[aria-hidden="false"] {
    display: block !important;
    height: auto;
}

Not sure if this is the right solution, but it worked for me, and it might help others, seeing this is the place you land, when you google the problem :)

@kenwheeler
Copy link
Owner

@JensNielsen81 Yeah its tough because if you are using display: none, slick can't get the correct dimensions from a hidden element. I'm certainly not going to poll for display changes, and there really aren't reliable events in place to recalc. Been thinking a lot about how to solve this one...

@Jastos
Copy link

Jastos commented Jan 29, 2015

The CSS solution by Jens worked great for me. Slick inside of Foundation Tabs was giving me a hard time. Thanks!

@beingtree
Copy link

thanks, @alexrussell: your solution worked for me using slick inside a Foundation 5 accordion.

@ghost
Copy link

ghost commented Feb 27, 2015

@alexrussell , sorry to ask, but would you be able to shed some light on how your code would be used with responsiveTabs? I've looked at their docs and some 'Events', but it doesn't seem to match up with Bootstraps. So far I've got:

$('[href="#before-after"]').on('shown.bs.tab', function (e) {
    $('.slick-carousel-responsive').resize();
});

The only thing I haven't changed so far is the shown.bs.tab. The docs for ResponsiveTabs is here: https://github.com/jellekralt/Responsive-Tabs#events

But I'm not sure if I simply have to replace shown.bs.tab with tabs-activate or another event shown.

Essentially, I believe I'm having hte same issues as everyone else, when the page loads with another tab being shown, the slick width is displaying as 0, even when the tab is selected.

Can you or anyone else help?

@alexrussell
Copy link
Author

@leecollings sorry for the delay getting back to you, I kept forgetting to come and read this properly after seeing it in my emails. I haven't tested this but from the demo given by the repo itself something like this will work:

$('#tabContainer').responsiveTabs({
    // ...
    activate: function(e, tab) {
        if (tab.selector == '#carousel') {
            $('.slick-carousel-responsive').resize();
        }
    },
    // ...
});

The if (tab.{something} == {something}) part can be any kind of check you want. In the example they give, if simply looks at tab.id, which gives you the 0-based tab index. In my book that's not safe to rely on, hence using tab.selector looking for a given id above. Looks like the tab object itself has a few things you can use to check whether or not your tab is the one selected (tab.id, tab.selector, tab.panel, tab.anchor to name the four most handy).

Also, instead of passing this function to the bootstrapping of responsiveTabs you could also use jQuery event binding as you suggest above:

$('#tabContainer').responsiveTabs({
    // ...
});

// ...

$('#tabContainer').on('tabs-activate', function(e, tab) {
    if (tab.selector == '#carousel') {
        $('#slick-carousel-responsive').resize();
    }
});

@LDigital84
Copy link

I ran into this issue recently and saw an answer on StackOverflow that worked great for me. The code:

$('.slick-slider').get(0).slick.setPosition();

IE:

$( ".news-link" ).on( "click", function() {
  $( ".news-container" ).slideToggle( "slow" );
  $('.news-items').get(0).slick.setPosition() //.news-items is class for the slider.
  return false;
});

@mufaddalmw
Copy link

@patrickvaillancourt your solution is tricky and worked for me, Thanks :)

@yangkennyk
Copy link

@Lynda333

worked wonderfully for me. thanks mucho!

@Marionovello
Copy link

This worked for me (directly from Slick site)
// Manually refresh positioning of slick
$('.your-element').slick('setPosition');

@jamesonnuss
Copy link

I am using JQuery UI Tabs and was struggling with this issue all day. Thank you!! @JensNielsen81 The CSS works perfectly.

@moog16
Copy link

moog16 commented Oct 2, 2015

@Marionovello that worked perfectly for me! .resize() was working for me on Chrome/Firefox, but not on IE9. setPosition did the trick.

@camiloperezv
Copy link

i solved in this way (coffeescript)

angular.element('#new-report').show( "slow",()->
angular.element('#slider-report').get(0).slick.setPosition();
);

@steal9pro
Copy link

@patrickvaillancourt thank you, man

@JeremyLopez
Copy link

I didn't use bootstrap and wrote my own equivalent to bootstrap tabs, but when I try to fire $(this).slick("getSlick").refresh() when they click the tab, it doesn't work. Is there anything else I can do?

@mariovalney
Copy link

@JeremyLopez try @alexrussell suggestion:

$('.your-tab-trigger').on('click', function (e) {
    $('.your-slick').resize();
});

@samjonesigd
Copy link

$('.your-slick').slick("getSlick").refresh();

this works great on click and a few other suggestions worked too on opening and closing tabs, but if i resize the page then open a tab my slick is broke until it goes to the next slide. This could be an issue when people are changing orientation on devices. Any suggestions?

@hot-Whiskey
Copy link

hot-Whiskey commented May 2, 2017

@TimmyDodd worked for me! thank you! also, changing event handler from 'show.bs.modal' to 'shown.bs.modal' works (comes with lag, though)

@gety9
Copy link

gety9 commented May 10, 2017

patrickvaillancourt, thanks!

Your solution worked for me!

@tijanmdr
Copy link

tijanmdr commented Jul 3, 2017

Thanks to @painreign.. The solution works

$('.slick-div-holder').each(function() {
     $(this).slick("getSlick").refresh();
});

But this flickers alot

@AbdallahNasser
Copy link

unfortunately, I tried all the solutions above, but none of them worked correctly with me
In my case, I have 2 tab containers in the same page, so when I try the bootstrap hack, it works for the slick, but it results an overflow within the other tab container (tab panes are overflowing each other)
is there any thing else to do ?

@leggomuhgreggo
Copy link
Collaborator

leggomuhgreggo commented Jul 29, 2017

@AbdallahNasser So the reason it breaks is because by default the content in a non-active tab is hidden with display: none, which prevents the slick JS from getting the dimensions properly.

So you can use visibility hidden, which hides things without taking them out of the document flow, and position absolute all except one of the .tab-panes so only one of them is setting height in the document flow.

.tab-content>.tab-pane {display: block;visibility: hidden;}
.tab-content>.tab-pane.active {visibility: visible;}
.tab-content{position: relative;}
.tab-pane:not(:first-child){position: absolute;width: 100%;height: 100%;top: 0;left: 0;}

Example

Note: This assumes all the tab panes have the same height, which might not be the case. If each tab pane needs to be able to set the height of the parent through the document flow, then you should probably use a horizontal offset to hide/show.

@tibelchior
Copy link

None of the above worked in my case, I had to set a timeout to execute the refresh.

$('.depos').on('click', function () {
	setTimeout(function() {
	   $('.slider-nav').slick("getSlick").refresh();
	   $('.slider-for').slick("getSlick").refresh();
	},300);
});

@tijanmdr
Copy link

tijanmdr commented Sep 19, 2017

@tibelchior Yeah, to work with slick with AJAX requests, u always have to set timeouts or refresh or deactivate and activate slick.

Better to use other sliders rather than slick.

@tibelchior
Copy link

@tijanmdr which one do you recommend that is as good as Slick?
I'm having special problem to execute Animate.css. It just works in the 1st slide

@tijanmdr
Copy link

I think Owl Carousel is also a good one. Try that one out.

@kabelo38
Copy link

this worked for me .nav-item its a class found on tabs menu

       $('.nav-item').click(function() {

            $('.slick-disabled').trigger('click');
        })

@mladimatija
Copy link

If anyone is still having this issue - seems like if you change the .slick-active slide in any way via JS after initialization of slick slider it will work as expected.

So, after you initialize your slider just add for example yourSlider.find('.slick-active').css('z-index', 1); and it will will work :)

@kstratis
Copy link

@patrickvaillancourt @JensNielsen81 Thanks for the elegant solution!
I spent half a day of frustration and experimentation with tens of slick suggestions to no avail.
Your pure css solution was the only one that truly worked for me the way I wanted it to.

@TwelveLabours
Copy link

SIMPLE SOLUTION Thanks @LDigital84

I have a site where 'slick' is within a hidden div, and then shown during a transition. Slick's parent div is set to 'hidden' with initial CSS, and shown later with JQuery. Slick wouldn't appear, even though during initial formatting and testing (with the parent div NOT hidden) Slick had been performing as expected.

I simply use a hacky function call:

function slickFix(){
$('.yourSlickClass').get(0).slick.setPosition();
}

and then call that function after I show the parent div.

Works a charm.....thanks, this was driving me crazy!

@magicjoef
Copy link

I wasn't getting very far with the setPosition or triggering resize approaches, maybe because I had a slightly different context. Just in case this helps anyone...

I needed to switch between a few different slick sliders, meaning I wanted a couple of hidden sliders on load that I swapped in on a button click. However, using display none leads to the cited problem of Slick not being able to get dimensions.

I thought about flashing them in on load and then hiding them, as they are near the bottom of my page, that way Slick could init them. However, that's not tidy and still left the issue of what happened if someone resized the browser. All the hidden sliders wouldn't be resized correctly.

In the end I decided to create Slick everytime I needed it. Using a combination of a jQuery fadeIn followed by initialising slick worked well. I thought it might flash content, but it hasn't in the browsers I've tested.

When I swap to a new slick carousel, I hide the existing one and unslick it. Then for the incoming carousel I use the combination of fadeIn and initialising. Kind of a just in time approach. Something to consider depending where and how you're using Slick.

It seems stable on what I've tested, but if anyone can see any pitfalls to this approach I haven't spotted please let me know.

@codacopia
Copy link

@painreign solution was the only thing that would work for me with multiple slide modals on a page.

@bomzj
Copy link

bomzj commented Aug 2, 2018

I implemented universal code for all type of containers: tabs, modals, collapsible's and etc that finds hidden parent container where slick is in and updates slick position each time parent container is displayed (display: block).

function isHidden(el) {
	var style = window.getComputedStyle(el);
	return style.display === 'none';
}

function findHiddenParent(el) {
	while (el) {
		el = el.parentElement;
		if (el && isHidden(el)) return el;
	}
}

function onParentChanged(slickSlider) {
	if (this.style.display != 'none') {
		$(slickSlider).slick('setPosition');
	}
}


var slickSlider = $(".slick").slick({...});

// Slick can not calculate its dimension while being placed in hidden modals, tabs, collapsibles and etc(display: none)
// When hidden parent gets visible, we should do slick resize() to display it appropriately
var hiddenParent = findHiddenParent(slickSlider);
if (hiddenParent) {
	var observer = new MutationObserver(onParentChanged.bind(hiddenParent, slickSlider));
	observer.observe(hiddenParent, { attributes: true });
}

@congson95dev
Copy link

Okay so i was stuck in this sitution just like you guys and i found the reason it cause the issue, it simple because when you use slick in tabs, it gonna set your unused tab to display: none, and when they did, the width in that unused tab become 0, and slick need a width to run correctly, and this solution worked for me:

.tab-content>.tab-pane{
  display: flex !important;
  height: 0px;
  overflow: hidden;
}

.widget-tab.mixed-tab .tab-pane.active{
  height: auto;
}

Set non-active tab to display: flex !important; and overflow: hidden; will do the same thing as display: none, but for some reason, it will set your width automatic, so the slick will works perfectly.

@bassm
Copy link

bassm commented Nov 29, 2018

.tab-content>.tab-pane{ display: block; height: 0px; overflow: hidden; }
.tab-content>.active{ height: auto;}

@zandiarash
Copy link

I solved my problem with slick after showing bootstrap modal :

$("#Modal").on('shown.bs.modal', function(){
    $('#Slider').slick();
})

$('#Button').click(function(){
    $('#Modal').modal('show');
});

maybe it helps.

@bbwstar
Copy link

bbwstar commented Jun 26, 2019

In my side above solutions are not working.
settimeout option ( @tibelchior ) is only working. Are there a better way without settimout function?

@khatrn
Copy link

khatrn commented Aug 3, 2019

@Arashz22 your code worked for me! Thank you! 👍

@zandiarash
Copy link

@Arashz22 your code worked for me! Thank you! 👍

Happy to hear that, you are always welcome😍

@bnikkhah
Copy link

bnikkhah commented Feb 4, 2024

The .resize() option didn't work for me. I experienced a similar problem attempting to render an embedded google map inside a bootstrap tab as well. To my surprise both problems were resolved by changing how bootstrap handles hidden tabs:

/* bootstrap hack: fix content width inside hidden tabs /
.tab-content > .tab-pane, .pill-content > .pill-pane {
display: block; /
undo display:none /
height: 0; /
height:0 is also invisible /
overflow: hidden; /
no-overflow /
}
.tab-content > .active, .pill-content > .active {
height: auto; /
let the content decide it /
} /
bootstrap hack end */
more here: https://groups.google.com/forum/#!topic/twitter-bootstrap-stackoverflow/0Aqzs5s_hEY

I was spending about 4 hours figuring this out and I cried for joy when I found this code. Thank you so MUCH!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests