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

Lazy Images: Drop jQuery, convert to vanilla JS #14886

Merged
merged 5 commits into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion 3rd-party/woocommerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,17 @@ function jetpack_woocommerce_infinite_scroll_style() {
function jetpack_woocommerce_lazy_images_compat() {
wp_add_inline_script( 'wc-cart-fragments', "
jQuery( 'body' ).bind( 'wc_fragments_refreshed', function() {
jQuery( 'body' ).trigger( 'jetpack-lazy-images-load' );
var jetpackLazyImagesLoadEvent;
try {
jetpackLazyImagesLoadEvent = new Event( 'jetpack-lazy-images-load', {
bubbles: true,
cancelable: true
} );
} catch ( e ) {
jetpackLazyImagesLoadEvent = document.createEvent( 'Event' )
jetpackLazyImagesLoadEvent.initEvent( 'jetpack-lazy-images-load', true, true );
}
jQuery( 'body' ).get( 0 ).dispatchEvent( jetpackLazyImagesLoadEvent );
Copy link
Contributor Author

@dero dero Mar 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: While it may feel like it, switching from jQuery trigger to dispatching native events does not introduce a regression. There are two notable differences in the usage of both:

  1. jQuery trigger creates proprietary events that are only observable using jQuery (on, bind etc.). Meaning that there is no 3rd party code out there using addEventListener to observe them.
  2. The native dispatchEvent is observable using both addEventListener and jQuery methods. Importantly, jQuery will normalize the incoming event and expose any additional detail data in a jQuery way.

This means any pre-existing handler is not going to be affected by a change from the proprietary trigger to native dispatchEvent. 🎉

All of the above is incorrect. See #14886 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the issue reported by @jeherve disproves the above. It now seems jQuery listeners (on, bind) do not pass the detail data as a second parameter to event handlers after all. I've however tested that behavior before and I'm not certain why I'm seeing a different outcome now. I will revisit this issue tomorrow.

} );
" );
}
Expand Down
46 changes: 44 additions & 2 deletions modules/infinite-scroll/infinity.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,12 @@

// Check if we can wrap the html
this.element.append( response.html );
this.body.trigger( 'post-load', response );

this.trigger( this.body.get( 0 ), 'is.post-load', {
jqueryEventName: 'post-load',
data: response,
} );

this.ready = true;
};

Expand Down Expand Up @@ -619,7 +624,10 @@
$set.css( 'min-height', '' ).removeClass( 'is--replaced' );
if ( this.pageNum in self.pageCache ) {
$set.html( self.pageCache[ this.pageNum ].html );
self.body.trigger( 'post-load', self.pageCache[ this.pageNum ] );
self.trigger( self.body.get( 0 ), 'is.post-load', {
jqueryEventName: 'post-load',
data: self.pageCache[ this.pageNum ],
} );
}
}
} );
Expand Down Expand Up @@ -716,6 +724,40 @@
this.disabled = false;
};

/**
* Emits custom JS events.
*
* @param {Node} el
* @param {string} eventName
* @param {*} data
*/
Scroller.prototype.trigger = function( el, eventName, opts ) {
opts = opts || {};

/**
* Emit the event in a jQuery way for backwards compatibility where necessary.
*/
if ( opts.jqueryEventName && 'undefined' !== typeof jQuery ) {
jQuery( el ).trigger( opts.jqueryEventName, opts.data || null );
}

/**
* Emit the event in a standard way.
*/
var e;
try {
e = new CustomEvent( eventName, {
bubbles: true,
cancelable: true,
detail: opts.data || null,
} );
} catch ( err ) {
e = document.createEvent( 'CustomEvent' );
e.initCustomEvent( eventName, true, true, opts.data || null );
}
el.dispatchEvent( e );
};

/**
* Ready, set, go!
*/
Expand Down
73 changes: 43 additions & 30 deletions modules/lazy-images/js/lazy-images.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* globals IntersectionObserver, jQuery */
/* globals IntersectionObserver */

var jetpackLazyImagesModule = function( $ ) {
var jetpackLazyImagesModule = function() {
var images,
config = {
// If the image gets within 200px in the Y axis, start the download.
Expand All @@ -12,15 +12,16 @@ var jetpackLazyImagesModule = function( $ ) {
image,
i;

$( document ).ready( function() {
lazy_load_init();
lazy_load_init();

var bodyEl = document.querySelector( 'body' );
if ( bodyEl ) {
// Lazy load images that are brought in from Infinite Scroll
$( 'body' ).bind( 'post-load', lazy_load_init );
bodyEl.addEventListener( 'is.post-load', lazy_load_init );

// Add event to provide optional compatibility for other code.
$( 'body' ).bind( 'jetpack-lazy-images-load', lazy_load_init );
} );
bodyEl.addEventListener( 'jetpack-lazy-images-load', lazy_load_init );
}

function lazy_load_init() {
images = document.querySelectorAll( 'img.jetpack-lazy-image:not(.jetpack-lazy-image--handled)' );
Expand Down Expand Up @@ -96,40 +97,48 @@ var jetpackLazyImagesModule = function( $ ) {
* @param {object} image The image object.
*/
function applyImage( image ) {
var theImage = $( image ),
srcset,
var srcset,
sizes,
theClone;
lazyLoadedImageEvent;

if ( ! theImage.length ) {
if ( ! image instanceof HTMLImageElement ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea

return;
}

srcset = theImage.attr( 'data-lazy-srcset' );
sizes = theImage.attr( 'data-lazy-sizes' );
theClone = theImage.clone(true);
srcset = image.getAttribute( 'data-lazy-srcset' );
sizes = image.getAttribute( 'data-lazy-sizes' );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lazy-loading functionality works well:

loop-in


// Remove lazy attributes from the clone.
theClone.removeAttr( 'data-lazy-srcset' ),
theClone.removeAttr( 'data-lazy-sizes' );
theClone.removeAttr( 'data-lazy-src' );
// Remove lazy attributes.
image.removeAttribute( 'data-lazy-srcset' ),
image.removeAttribute( 'data-lazy-sizes' );
image.removeAttribute( 'data-lazy-src' );

// Add the attributes we want.
image.classList.add( 'jetpack-lazy-image--handled' );
image.setAttribute( 'data-lazy-loaded', 1 );

// Add the attributes we want on the finished image.
theClone.addClass( 'jetpack-lazy-image--handled' );
theClone.attr( 'data-lazy-loaded', 1 );
if ( ! srcset ) {
theClone.removeAttr( 'srcset' );
} else {
theClone.attr( 'srcset', srcset );
}
if ( sizes ) {
theClone.attr( 'sizes', sizes );
image.setAttribute( 'sizes', sizes );
}

theImage.replaceWith( theClone );
if ( ! srcset ) {
image.removeAttribute( 'srcset' );
} else {
image.setAttribute( 'srcset', srcset );
}

// Fire an event so that third-party code can perform actions after an image is loaded.
theClone.trigger( 'jetpack-lazy-loaded-image' );
try {
lazyLoadedImageEvent = new Event( 'jetpack-lazy-loaded-image', {
bubbles: true,
cancelable: true
} );
} catch ( e ) {
lazyLoadedImageEvent = document.createEvent( 'Event' )
lazyLoadedImageEvent.initEvent( 'jetpack-lazy-loaded-image', true, true );
}

image.dispatchEvent( lazyLoadedImageEvent );
}
};

Expand Down Expand Up @@ -865,4 +874,8 @@ var jetpackLazyImagesModule = function( $ ) {
}(window, document));

// Let's kick things off now
jetpackLazyImagesModule( jQuery );
if ( document.readyState === 'interactive' || document.readyState === "complete" ) {
jetpackLazyImagesModule();
} else {
document.addEventListener( 'DOMContentLoaded', jetpackLazyImagesModule );
}
2 changes: 1 addition & 1 deletion modules/lazy-images/lazy-images.php
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ public function enqueue_assets() {
'_inc/build/lazy-images/js/lazy-images.min.js',
'modules/lazy-images/js/lazy-images.js'
),
array( 'jquery' ),
array(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, jquery doesn't appear when using a theme without it, like Twenty Nineteen:

jquery-not-here

JETPACK__VERSION,
true
);
Expand Down