react-lazyload icon indicating copy to clipboard operation
react-lazyload copied to clipboard

[QUESTION]: lazyload-wrapper class

Open simplecommerce opened this issue 4 years ago • 33 comments

Hi @ameerthehacker, in the latest commit, you merged a pull request that added this span with the className lazyload-wrapper.

Is there a reason for this?

The previous behavior when the children was visible, was not to have any placeholder or wrapper around them.

Now because of this commit, it breaks my layout because of the extra span element with that class.

Is this the intended behavior?

Previous code:

return this.visible ? this.props.children : this.props.placeholder ? this.props.placeholder : _react2.default.createElement('div', { style: { height: this.props.height }, className: 'lazyload-placeholder' });

Now:

return _react2.default.createElement(
        'span',
        { className: 'lazyload-wrapper', ref: this.setRef },
        this.visible ? this.props.children : this.props.placeholder ? this.props.placeholder : _react2.default.createElement('div', {
          style: { height: this.props.height },
          className: 'lazyload-placeholder'
        })
      );

Please let me know, thanks!

simplecommerce avatar Jun 18 '20 13:06 simplecommerce

Hi @simplecommerce this span is necessary to avoid usage of findDom API and ReactRef actually needs a dom element in the jsx, the className in the span tag is provided for the same purpose for you to change its styling. Hope this clarifies that

ameerthehacker avatar Jun 18 '20 13:06 ameerthehacker

@ameerthehacker yes I understand, this change causes me problems because the previous behavior allowed us to apply styles to children without having to think of there is going to be an extra div/span around them, in the case of nesting of the lazy load component, it makes it impossible to know how to style it unless you know how the code was used, since the CMS we use it with is used by the customers.

is there a huge performance gain for this reason for the switch?

simplecommerce avatar Jun 18 '20 14:06 simplecommerce

@simplecommerce the findDom was deprecated and it was causing warning in lot of productions that is why we switched to React Ref. I can understand your concern that the consumers manipulate the code for their needs, in those cases I would suggest two thins. Try to add some styling which nullifies the span tag

.lazyload-wrapper {
...
}

or please switch to the previous version which does not break your code.

ameerthehacker avatar Jun 18 '20 14:06 ameerthehacker

@simplecommerce for your reference https://github.com/twobin/react-lazyload/issues/303 we ran a beta and then switched to stable but I honestly did not anticipate this

ameerthehacker avatar Jun 18 '20 14:06 ameerthehacker

@ameerthehacker yes that is what I am doing for the moment, I will try to figure out if there is a way I can re-factor my code to include this new approach, thanks!

simplecommerce avatar Jun 18 '20 14:06 simplecommerce

@ameerthehacker Thanks for your work on this library. Unfortunately, the change from 2.6.7 to 2.6.8 broke layouts on our site too due to the new wrapper element. Does it make sense to use a major version change here? I imagine there will be a lot of people who are surprised by this behavior and end up with broken CSS selectors.

danielpquinn avatar Jun 18 '20 20:06 danielpquinn

@danielpquinn I'm also starting think on the same line, if more people are affected I will make this into a Major update

ameerthehacker avatar Jun 19 '20 05:06 ameerthehacker

will keep this open for few more weeks

ameerthehacker avatar Jun 19 '20 05:06 ameerthehacker

Thanks for updating it!

But, I think a major version is in order. I didn't expect tons of snapshots to break in a patch update.

gilbarbara avatar Jun 20 '20 00:06 gilbarbara

@gilbarbara did it break your production too?

ameerthehacker avatar Jun 21 '20 07:06 ameerthehacker

Hey @ameerthehacker

I reverted to the previous version since it broke my snapshots. But it would definitely break the UI to have a wrapping span since I have styling applied to the immediate children.

gilbarbara avatar Jun 21 '20 14:06 gilbarbara

@gilbarbara thanks for letting me know, will revert back to old version and will make this as major

ameerthehacker avatar Jun 21 '20 15:06 ameerthehacker

Could you consider also at least changing the tag from a span to a div?

Span tags are inline-level elements and should only contain "phrasing content". If they are used to wrap block-level elements such as div tags then the HTML structure is invalid. In many usage cases users will end up wrapping block-level content which will result in an invalid HTML structure.

https://html.spec.whatwg.org/#the-span-element https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content

https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements "Content model Generally, inline elements may contain only data and other inline elements. You can't put block elements inside inline elements."

https://en.wikipedia.org/wiki/Span_and_div "For example, regardless of CSS, a span element may not contain block-level children"

kbradley avatar Jun 26 '20 20:06 kbradley

@kbradley that is a really good point thanks

ameerthehacker avatar Jun 27 '20 07:06 ameerthehacker

@ameerthehacker Thanks for the explanation!

Is there no way to remove the wrapper upon completing the lazy load, at least for cases where once={true}? There's a number of downsides for end users, though they're not deal-breaking.

We're trying to reduce our total DOM node count and depth, and so this adds hundred or more elements to our page. The recommendation is to keep total DOM nodes to 1,500 or less, so that's nearly 7% of the total. It's not like 1500 is a brick wall, but it would be highly beneficial for us if we could somehow remove the wrapper once the load is complete.

The main thing we use React-Lazyload for actually, is reducing DOM nodes, sort of like an easy to implement alternative to React-Waypoint. I imagine this is not the way most people are using it, but the DOM node count concern remains.

While I'm writing this anyway, I want to also speak up in support of changing the span to a div.

Thanks for your great work @ameerthehacker!

Nantris avatar Jun 28 '20 00:06 Nantris

@Slapbox thanks for your inputs, I totally agree with you and I'm currently looking into ways by which we can totally eliminate the extra node itself, will keep posting the updates

ameerthehacker avatar Jun 28 '20 09:06 ameerthehacker

@simplecommerce @gilbarbara I have reverted back the old change as 2.6.9 and have released the breaking change as 3.0.0 to npm, @kbradley I have switched span tag with div. @Slapbox currently I'm not sure how to avoid the extra DOM node overhead but will keep thinking on it and meantime if you find any way to improve it feel free to raise a PR.

ameerthehacker avatar Jun 28 '20 10:06 ameerthehacker

@ameerthehacker thanks for your speedy replies and updates!

As far as avoiding the extra DOM node before loading the content, I don't see any way right now - but I'm not clear on what purpose the wrapper serves once the content has finished loading (at least when once={true}. Can you help me understand the benefit at that stage?

Nantris avatar Jun 28 '20 18:06 Nantris

@Slapbox no when we use once={true} I don't see any purpose for the div tag but copying the children and replacing div tag will make the performance even bad?

ameerthehacker avatar Jun 29 '20 05:06 ameerthehacker

copying the children and replacing div tag will make the performance even bad?

Most likely it would be worse for performance, yeah. I just wanted to make sure I understood the purpose of the wrapper before trying to offer any ideas (if any do occur to me.)

Thanks again @ameerthehacker!

Nantris avatar Jun 29 '20 18:06 Nantris

Hi I can't find a prop for LazyLoad so it knows the array's length so it doesn't show the placeholder when it reaches the end of the array . How can I tell it dont show the placeholder={

loading...

} when you've reached the end of the array ?

gittestfor avatar Jul 13 '20 02:07 gittestfor

@gittestfor it doesn't seem like your comment is related to the topic of this issue thread. Maybe I'm misunderstanding.

Nantris avatar Jul 13 '20 20:07 Nantris

Hi, I just want to say that ... The wrapper is a terrible idea 😅

Making it a div is even worse. For example, div isn't valid within p (where you'd expect to have an img, for example). A solution to this problem could be to allow the component to accept a prop as, so the wrapper could be defined as any element, block or inline.

Here is my quick attempt at finding an alternative to findDOMNode that may be helpful .. or inspire a better idea 😅

const LazyThing: React.FC = ({ children }) => {
  const refDOMNode = React.useRef<Element | null>();

  const transformToComment = (e: HTMLSpanElement) => {
    if (!e) return;
    const comment = document.createComment(e.innerHTML);
    e.parentNode?.replaceChild(comment, e);
    return comment;
  };

  const findDOMNode = (e: HTMLSpanElement) => {
    if (!e) return;

    const comment = transformToComment(e)!;

    if (
      comment.nextElementSibling?.nodeType !== 8 &&
      comment.nextElementSibling?.textContent !== "END"
    ) {
      refDOMNode.current = comment.nextElementSibling;
      console.log("findDOMNode:", refDOMNode.current); // <<< ta-da!
    } else {
      refDOMNode.current = null;
    }
  };

  return (
    <>
      <span key="s" ref={findDOMNode}>
        START
      </span>
      {children}
      <span key="e" ref={transformToComment}>
        END
      </span>
    </>
  );
};

export default function App() {
  return (
    <div className="App">
      <LazyThing>
        <div>Example</div>
      </LazyThing>
    </div>
  );
}

eddyw avatar Jul 16 '20 20:07 eddyw

Thanks for the feedback @eddyw, your idea on the first look seems great, can you please raise a PR to do the same and if it works I will get it released

ameerthehacker avatar Jul 17 '20 05:07 ameerthehacker

At the very least, I think we need to be able to configure the selected wrapper element type, This change breaks even straightforward table row lazy loading...

EricMCornelius avatar Jul 29 '20 18:07 EricMCornelius

@EricMCornelius yes this is scheduled for next release

ameerthehacker avatar Jul 29 '20 18:07 ameerthehacker

Any update on this next release?

cwagner22 avatar Sep 12 '20 06:09 cwagner22

+1 From my side... Out app is using react-lazyload as well... and the wrapper just caused a whole world of pain... I'm reverting back to 2.6.5 for now

rohan-buechner avatar Dec 15 '20 12:12 rohan-buechner

Thank you so much Ameerthehacker for your contribution!

Sorry that I am new to react so may ask a stupid question. I applied "react-lazyload" in my app. It works for lazy loading a component containing img (map loop). However, when I add css (float: left;) to my app's component, the lazy loading not work (it will load all the img at the same time)!

Am I missing to use some Props? (I just use height and width and it has no issue if I don't use float: left)

And I also check the element in Chrome Debug mode, just found NO height at all for "lazyload-wrapper". And the height will appear if no "float: left" is used...

cheunghoman2 avatar Feb 05 '21 16:02 cheunghoman2

At the very least, I think we need to be able to configure the selected wrapper element type, This change breaks even straightforward table row lazy loading...

Do you have workaround for this issue?

ykhoe avatar Jul 23 '21 05:07 ykhoe