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

Add option to skip image src replacement if images are currently hidden #122

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
20 changes: 19 additions & 1 deletion Imager.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
this.scrollDelay = opts.scrollDelay || 250;
this.onResize = opts.hasOwnProperty('onResize') ? opts.onResize : true;
this.lazyload = opts.hasOwnProperty('lazyload') ? opts.lazyload : false;
this.loadHidden = opts.hasOwnProperty('loadHidden') ? opts.loadHidden : true;
this.scrolled = false;
this.availablePixelRatios = opts.availablePixelRatios || [1, 2];
this.availableWidths = opts.availableWidths || defaultWidths;
Expand Down Expand Up @@ -308,7 +309,7 @@
this.refreshPixelRatio();

applyEach(images, function (image) {
if (filterFn(image)) {
if (self.isImageEligibleForReplacing(image) && filterFn(image)) {
self.replaceImagesBasedOnScreenDimensions(image);
}
});
Expand All @@ -318,6 +319,10 @@
}
};

Imager.prototype.isImageEligibleForReplacing = function (image) {
return this.loadHidden || Imager.isElementVisible(image);
};

/**
* Upgrades an image from an empty placeholder to a fully sourced image element
*
Expand Down Expand Up @@ -457,6 +462,19 @@
}
};

/**
* Elements are considered visible if they consume space in the document.
* Visible elements have a width or height that is greater than zero.
* Elements with visibility: hidden or opacity: 0 are considered visible,
* since they still consume space in the layout.
*
* @param {HTMLElement} el
* @returns {boolean} Whether or not the element is visible
*/
Imager.isElementVisible = function (el) {
return el.offsetWidth > 0 || el.offsetHeight > 0;
};

/**
* Returns the naturalWidth of an image element.
*
Expand Down
19 changes: 19 additions & 0 deletions test/fixtures/hidden.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<style>
div { height: 10px; }
#hidden-element-implicit { height: 0; width: 0; }
</style>

<div id="visible-element" class="delayed-image-load" data-src="base/test/fixtures/media/A-320.jpg"></div>

<div id="main" style="display: none;">
<div id="hidden-child" class="delayed-image-load" data-src="base/test/fixtures/media/B-320.jpg"></div>
<div class="child">
<div id="hidden-sub-child" class="delayed-image-load" data-src="base/test/fixtures/media/B-640.jpg"></div>
</div>
</div>

<div id="hidden-element" style="display: none;" class="delayed-image-load" data-src="base/test/fixtures/media/C-320.jpg"></div>
<div id="hidden-element-visibility" style="visibility: hidden;" class="delayed-image-load" data-src="base/test/fixtures/media/C-320.jpg"></div>
<div id="hidden-element-implicit" class="delayed-image-load" data-src="base/test/fixtures/media/C-320.jpg">
<!-- no height -->
</div>
73 changes: 73 additions & 0 deletions test/unit/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,4 +276,77 @@ describe('Imager.js', function () {
expect(imgr.isThisElementOnScreen(element)).to.equal(true);
});
});

describe('checkImagesNeedReplacing', function () {

it('ignores images which are hidden when loadHidden is set to false', function () {
var imager = new Imager({
loadHidden: false
}),
imageVisibilities = [true, false, true];

sandbox.stub(imager, 'refreshPixelRatio');
sandbox.stub(Imager, 'isElementVisible').returnsArg(0);
sandbox.stub(imager, 'replaceImagesBasedOnScreenDimensions');

imager.checkImagesNeedReplacing(imageVisibilities);

// Only the first and third image should be replaced
expect(imager.replaceImagesBasedOnScreenDimensions.callCount).to.equal(2);
expect(imager.replaceImagesBasedOnScreenDimensions.getCall(0).args[0]).to.equal(true);
expect(imager.replaceImagesBasedOnScreenDimensions.getCall(1).args[0]).to.equal(true);
});

it('replaces all images if loadHidden is set to true (default)', function () {
var imager = new Imager(),
imageVisibilities = [true, false, true];

sandbox.stub(imager, 'refreshPixelRatio');
sandbox.stub(Imager, 'isElementVisible').returnsArg(0);
sandbox.stub(imager, 'replaceImagesBasedOnScreenDimensions');

imager.checkImagesNeedReplacing(imageVisibilities);

// All images should be loaded
expect(imager.replaceImagesBasedOnScreenDimensions.callCount).to.equal(3);
expect(imager.replaceImagesBasedOnScreenDimensions.getCall(0).args[0]).to.equal(true);
expect(imager.replaceImagesBasedOnScreenDimensions.getCall(1).args[0]).to.equal(false);
expect(imager.replaceImagesBasedOnScreenDimensions.getCall(2).args[0]).to.equal(true);
});
});

describe('isElementVisible', function () {

it('should return true if element is visible', function () {
fixtures = loadFixtures('hidden');
expect(Imager.isElementVisible(fixtures.querySelector('#visible-element'))).to.equal(true);
expect(Imager.isElementVisible(fixtures.querySelector('#visible-element'))).to.equal(true);
});

it('should return true if the element is set to visiblity: hidden', function () {
fixtures = loadFixtures('hidden');
expect(Imager.isElementVisible(fixtures.querySelector('#hidden-element-visibility'))).to.equal(true);
});

it('should return false if the element itself is display: none', function () {
fixtures = loadFixtures('hidden');
expect(Imager.isElementVisible(fixtures.querySelector('#hidden-element'))).to.equal(false);
});

it('should return false if the element has no height or width', function () {
fixtures = loadFixtures('hidden');
expect(Imager.isElementVisible(fixtures.querySelector('#hidden-element-implicit'))).to.equal(false);
});


it('should return false if the element is within a display: none element', function () {
fixtures = loadFixtures('hidden');
expect(Imager.isElementVisible(fixtures.querySelector('#hidden-child'))).to.equal(false);
});

it('should return false if the element is within a child of a display: none element', function () {
fixtures = loadFixtures('hidden');
expect(Imager.isElementVisible(fixtures.querySelector('#hidden-sub-child'))).to.equal(false);
});
});
});