From 278ba9e4decbbf4dccbbe11acc22042b0138f4c0 Mon Sep 17 00:00:00 2001 From: Chris James Date: Wed, 18 Feb 2015 16:29:12 +0000 Subject: [PATCH 1/3] Add option to not replace images if they are hidden --- Imager.js | 20 ++++++++++- test/fixtures/hidden.html | 19 ++++++++++ test/unit/core.js | 73 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/hidden.html diff --git a/Imager.js b/Imager.js index 19d3c1b..009332d 100644 --- a/Imager.js +++ b/Imager.js @@ -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; @@ -308,7 +309,7 @@ this.refreshPixelRatio(); applyEach(images, function (image) { - if (filterFn(image)) { + if (self.isImageEligibleForReplacing(image) && filterFn(image)) { self.replaceImagesBasedOnScreenDimensions(image); } }); @@ -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 * @@ -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 && el.offsetHeight); + }; + /** * Returns the naturalWidth of an image element. * diff --git a/test/fixtures/hidden.html b/test/fixtures/hidden.html new file mode 100644 index 0000000..f544cc2 --- /dev/null +++ b/test/fixtures/hidden.html @@ -0,0 +1,19 @@ + + +
+ + + + + +
+ +
\ No newline at end of file diff --git a/test/unit/core.js b/test/unit/core.js index deaf5e9..0a69135 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -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', 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); + }); + }); }); From ad35794526a867bce3cf7b36624aa48d70175fe4 Mon Sep 17 00:00:00 2001 From: Chris James Date: Wed, 18 Feb 2015 16:38:34 +0000 Subject: [PATCH 2/3] Fix Imager.isElementVisible: Opera reports offsetWidths and offsetHeights less than zero on some elements --- Imager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Imager.js b/Imager.js index 009332d..dfa2178 100644 --- a/Imager.js +++ b/Imager.js @@ -472,7 +472,7 @@ * @returns {boolean} Whether or not the element is visible */ Imager.isElementVisible = function (el) { - return !!(el.offsetWidth && el.offsetHeight); + return el.offsetWidth > 0 && el.offsetHeight > 0; }; /** From 6985075180ca15abfe494d8442fd1ab74f87e593 Mon Sep 17 00:00:00 2001 From: Chris James Date: Tue, 3 Mar 2015 10:01:16 +0000 Subject: [PATCH 3/3] Change isElementVisible function to return true of the element has a width or height as opposed to both --- Imager.js | 2 +- test/fixtures/hidden.html | 2 +- test/unit/core.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Imager.js b/Imager.js index dfa2178..e181974 100644 --- a/Imager.js +++ b/Imager.js @@ -472,7 +472,7 @@ * @returns {boolean} Whether or not the element is visible */ Imager.isElementVisible = function (el) { - return el.offsetWidth > 0 && el.offsetHeight > 0; + return el.offsetWidth > 0 || el.offsetHeight > 0; }; /** diff --git a/test/fixtures/hidden.html b/test/fixtures/hidden.html index f544cc2..d089aab 100644 --- a/test/fixtures/hidden.html +++ b/test/fixtures/hidden.html @@ -1,6 +1,6 @@
diff --git a/test/unit/core.js b/test/unit/core.js index 0a69135..e837ae7 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -333,7 +333,7 @@ describe('Imager.js', function () { expect(Imager.isElementVisible(fixtures.querySelector('#hidden-element'))).to.equal(false); }); - it('should return false if the element has no height', function () { + 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); });