React-slick: zero height(1px) image(GIF) problem with lazyLoad and adaptiveHeight

Created on 27 Jun 2016  路  29Comments  路  Source: akiran/react-slick

When I turn on the both lazyLoad and adaptiveHeight options,
sometimes the image(GIF) has 1px height.

Then, I click somewhere in carousel area, it re-render image properly.

I think that it occurs because the image(GIF) is not fully loaded yet.
(this is the reason why this happens 'sometimes')

If I turn off one of the lazyLoad or adaptiveHeight option everything works fine.

Could you fix this problem?

Most helpful comment

Here is what worked for me :

  componentDidMount() {
    const imgs = ReactDOM.findDOMNode(this.slider).querySelectorAll('img');
    let loadedImgs = 0;
    imgs.forEach((img) => {
      img.onload = () => {
        loadedImgs += 1;
        if (loadedImgs === imgs.length) {
          this.slider.innerSlider.adaptHeight();
        }
      };
    });
  }

All 29 comments

Similar Issue can be reproduced in fiddle example. Slider container calculating incorrect height because of images not yet loaded.

The partial solution is min-height for fixed container but Any fix for the dynamic container ?

https://jsfiddle.net/kirana/20bumb4g/

image

the usual solution I see for this is calculating the height again in the onload of images

Here is what the original Slick uses, recalculates height in image.onload if adaptive height is enabled. Could probably do a similar thing here

@psimyn:
nice comment.
then, do I have to use refs or findDOMNode?

I think calling adaptHeight will do that for you

I tried adaptiveHeight: true, but it didn't work. For whatever reason the element style is being set to 1px

@cramhead, I meant that adaptiveHeight calls a function in react-slick called adaptHeight (that's what the link goes to). It'll need a pull request, I'll have a shot at the fix over the weekend

Way to fix this issues

const ReactSlider = React.createClass({

getInitialState() {
        return {
            adaptiveHeight: false
        }
    },

    reRender() {
        setTimeout(() => {
           this.setState({
               adaptiveHeight: true
           })
        }, 0) =====> 200 
    },

  componentDidMount() {
        this.reRender();
    },

  render() {
     <Slider  adaptiveHeight={this.state.adaptiveHeight} {...settings}>
   }
});

Also happens here

change 0 timeout to 200, in firefox 0 timeout does not work

I am using Chrome, but didn't work as well

This issue still occurs, even when lazyLoad and adaptiveHeight are both false.
Anyone had any luck?

You need to adaptHeight after images load. Can start with something like this from componentDidMount of inner-slider

componentDidMount() {
  // ..other stuff
    const imgs = findDOMNode(this.refs.list).querySelectorAll('img')
    let loadedImgs = 0
    imgs.forEach(img => {
      const newImg = new Image()
      newImg.src = img.src
      newImg.onload = () => {
        loadedImgs++
        if (loadedImgs === imgs.length) {
          this.adaptHeight();
        }
      }
    })
// ...rest

there is still the issue of height jumping when scrolling, I'll have a bit more of a look

What do you mean by "inner-slider"?

@saharhashai - https://github.com/akiran/react-slick/blob/master/src/inner-slider.jsx#L44

You can reproduce with adaptiveHeight: false ?

@psimyn Yep. My settings is as follows -

const settings = {
      dots: false,
      arrows: false,
      speed: 500,
      infinite: false,
      slidesToShow: 1,
      slidesToScroll: 1,
      adaptiveHeight: false,
      afterChange: this.selectedEntityChanged.bind(this),
      slickGoTo: this.state.slickGoTo
    }

can you try with this version https://github.com/psimyn/react-slick/tree/img-onload

npm i psimyn/react-slick#img-onload

It has a change similar to the one above to update the heights as images have loaded. I think the better option is to monitor the size of the element and update when it changes (this could be used to replace the current resize listeners as well)

Can you wrap img tag with div and see if this issue still exists

Check this example
https://jsfiddle.net/kirana/20bumb4g/

it still happens if wrapped - I've been reproducing it by having a few images per slide. Easiest way I can reproduce is with lazyLoad and throttle network in Chrome

Any progress on this? I am running into this issue with adaptiveHeight turn on and lazy load turned off. Not sure if this is related or not but while investigating the issue I ran into these two issues with the regular slick:
https://github.com/kenwheeler/slick/issues/1930
https://github.com/kenwheeler/slick/issues/771

I haven't had time to look at the actual react-slick code at all but right now I have a super hacky fix that just triggers a resize event as soon as the page is done loading which forces react-slick to recalculate the height. Definitely not something I want to stick with, but gotta launch soon and got enough other things to deal with.

As I said my workaround is extremely hacky but if it will help you until this really gets fixed here it is:

componentDidMount() {
  if (window.addEventListener) {
    window.addEventListener('load', this.resizeWindow);
  } else {
    window.attachEvent('onload', this.resizeWindow);
  }
}
componentWillUnmount() {
  if (window.addEventListener) {
    window.removeEventListener('load', this.resizeWindow);
  } else {
    window.detachEvent('onload', this.resizeWindow);
  }
}
resizeWindow() {
  if (typeof window.Event === "function" ) {
    window.dispatchEvent(new Event('resize'));
  } else {
    let evt = window.document.createEvent('UIEvents');
    evt.initUIEvent('resize', true, false, window, 0);
    window.dispatchEvent(evt);
  }
}

This is in the component I have wrapping the react-slick carousel. Full disclosure the code for actually triggering the resize event is straight off of stack overflow but is supposed to work for all browsers (haven't got around to testing anything but chrome yet). Because this is an on load event the rest of your page is going to load and then the carousel will fix itself. One of the many reasons this is not a long term solution and just a hack.

thanks @techwes - that still won't work for lazy-loaded images though. Similar issue to the setTimeout option is it still doesn't consider when images are actually ready. As I understand it, the problem is:

  • react-slick measures its contents and sees nothing there
  • sets slide height to 0 (or ~10px)
  • images finish loading
  • react-slick has no idea, so doesn't update

Some Possible solutions:

  1. onload handlers for all images. Detect when all the images in a slide have loaded, _then_ update the height. This was the gist of what I described here . It doesn't deal with other content types (e.g lazily-loaded text content)
  2. Placeholders for images - you can set fixed aspect ratio for your images using padding hacks. See "The Percentage Padding Ploy" here. This way will work without needing any react-slick changes, you just need wrapper elements for images
height: 0;
padding-top: 62.5%;
position: relative;
  1. Slide height based on slide contents. Either check Element.scrollHeight periodically, or try some kind of Element Resize Detection
  2. Don't set slide height. Or set height to auto after changing slides.

I'm facing this problem too.

+1

@RyoIkarashi it didn't fix it for me, I have images in my slides which are loaded when the slide gets visible. This leads to a quick height change, which looks ugly.

Here is what worked for me :

  componentDidMount() {
    const imgs = ReactDOM.findDOMNode(this.slider).querySelectorAll('img');
    let loadedImgs = 0;
    imgs.forEach((img) => {
      img.onload = () => {
        loadedImgs += 1;
        if (loadedImgs === imgs.length) {
          this.slider.innerSlider.adaptHeight();
        }
      };
    });
  }

What I ended up doing was adding a "imageLoaded" state property to my component and then setting the value in an onLoad callback for the image. That causes the component to re-render once the image has been loaded and it's all good then. I'm only caring about the first image being loaded as I only show one at a time but you might want to wait for all the images.

Problems related to this have been fixed with resize observer and image onload updates.
Please feel free to request reopen if disagree.

Still having this issue on 0.22.3. Main page is making three AJAX requests in three separate components, with the first in the order loading properly.

Was this page helpful?
0 / 5 - 0 ratings