callmecavs / layzr.js

A modern lazy loading library for images.

Home Page:http://callmecavs.com/layzr.js/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Suggestions

tjbenton opened this issue · comments

I like how this doesn't require any dependencies!! I also like how performant it is, along with the small file file size, great work! I have a few suggestions to improve performance, extend functionality, and (potentially) decrease file size.

  1. Remove elements from this._nodes after they have been revealed because you no longer need to check if they're in the viewport in update.

  2. Currently the _destroy method is not being used at all. This should run when this._nodes.length is 0. So you can remove the overhead of those events.

  3. Instead of defining this._nodes in the constructor I would move it to _create and just add an argument to _create(selector). If selector is undefined then just use the initial selector that was passed into the options. This way you can call Lazyr._create in some other function after the page loads and add other elements to this._nodes list. This would be useful if you ajaxed in images that weren't on the page when Lazyr was initially called. I would also update the name to be something else to be more semantic if you decide to implement it like this.

  4. In addition to #3 update document.querySelectorAll(this._optionsSelector) to be this._optionsContainer.querySelectorAll(selector || this._optionsSelector) so that you're only getting elements inside of the defined container.

  5. Remove this updateSelector because it's not being used and just adding weight to the file size.

  6. Remove getContainerHeight from _inViewport and just set this.containerHeight = this._optionsContainer.innerHeight || this._optionsContainer.offsetHeight inside of update outside of the loop. The reason for this is so that you don't have to get the container height for every node in this._nodes since it's highly unlikely that the container height will change before the update function is complete.

  7. Move the hidden attribute check into the update for loop. This way you don't have to waste time doing the calculations to check if it's visible if it's hidden.

    if(!node.hasAttribute(this._optionsAttrHidden) && this._inViewport(node)) {
    ...
    }
    

    Those are my 2 cents.

@tjbenton thanks for the suggestions man. great stuff all around, and really appreciate the nicely written issue.

currently working on v1.5.0 which is more or less a complete rewrite (see the branch if you're interested). might end up making it v2.0.0, because I think implementing some of this stuff will break the existing API. feedback specific to each of the suggestions below:

  • 1 - will be in v1.5.0
  • 2, 3 - will be in v1.5.0. i'll check the list of elements length in reveal, and call destroy if its 0. create will be written such that it can be called multiple times, and ill use a property to check if the event handlers are currently bound or not, to prevent them from being bound multiple times. in this case, it'd definitely make to rename it _init or _start
  • 4 - will be in v1.5.0 - really clever, nice work
  • 5 - updateSelector exists currently to accommodate people adding images dynamically, though its not well documented. i think suggestion 2 & 3 will address this, so I'll remove it
  • 5i - will be in v1.5.0 - great point, doesn't make sense to check container height for every element since it wont change mid-update

@callmecavs You're welcome. I added #7 after I posted the issue but you might consider removing this._optionsAttrHidden. I'm not sure when this would be useful. Because you have to declare data-lazyr and data-lazyr-hidden on an element which doesn't make sense when you can just not add data-lazyr for it to not run.

I'm adapting this lib to apply it to work with lazy showing items on the page. So that I can do what I did on this codepen and have it will run that animation when elements that are below the page come into view. Below is the solution that I'm using to handle responsive design states of elements. So that when elements are hidden on the page by a class of u-hide or something like that. Then become visible on a page when the screen resizes via a class of u-show@md. They will then still be lazy loaded on the page because they weren't removed from the nodes list.

/// @name is_hidden
/// @description 
/// Helper function to see if an element hidden via it's styles. It's used because of responsive sites.
/// @markup {js}
/// if(!is_hidden(somenode)){
///  // do something with the node that is not hidden
/// }
is_hidden(node){
 let style = window.getComputedStyle(node)
 return style.getPropertyValue("visible") !== "hidden" && style.getPropertyValue("display") !== "none";
}

I wrote it in an es6 class but it would be easy to change to es5.

You might also consider adding a debounce function to the event listeners http://davidwalsh.name/javascript-debounce-function

@tjbenton interesting. i see your point about removing the hidden attribute - the thinking there was that there may be some cases where you want Layzr to check an image consistently, but only sometimes reveal it. the benefit of the hidden attribute as it exists is that you dont need to call create or updateSelector again to have it start tracking the image - just remove the hidden attribute when its okay to show it and the next time its in the viewport itll reveal. you're right about the check being better off in the update function tho, i'll move it in the next version.

the handlers are currently debounced, using requestAnimationFrame - see the debounce helper functions and the HTML5 Rocks article they were adapted from

@callmecavs Potentially this would be a good time to think about making Layzr a bit more generic for scroll listeners in general #50 The use-case with this is one selector that will be pushed further down the page and revealed again.

@zslabs at the end of the day this is a library for lazy loading images, not for "doing stuff on scroll". a repeat option doesn't make sense because once an image has been loaded, there's no longer a need to track it - it cant load twice, nor would it make sense for it to do so.

that being said, using the suggestions i left you on #50, i think you can definitely adapt layzr to work as a "doing stuff on scroll" library for your use case. if you're looking for a library that is directly geared towards "doing stuff on scroll", i can personally recommend waypoints.js

any chance to know the release date for v2?

back on track to release v2, likely this week. will include srcset support! check my progress here: https://github.com/callmecavs/layzr.js/tree/v2.0.0