Skip to content

Commit b713204

Browse files
committed
Fix images loading by default if they were descendants of positioned elements
1 parent 602cdbf commit b713204

File tree

3 files changed

+63
-29
lines changed

3 files changed

+63
-29
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"description": " React Component to lazy load images using a HOC to track window scroll position. ",
55
"main": "build/index.js",
66
"peerDependencies": {
7-
"react": "^15.x.x || ^16.x.x"
7+
"react": "^15.x.x || ^16.x.x",
8+
"react-dom": "^15.x.x || ^16.x.x"
89
},
910
"devDependencies": {
1011
"babel-cli": "^6.24.1",
@@ -22,7 +23,6 @@
2223
"jest": "^22.4.0",
2324
"path": "^0.12.7",
2425
"react": "^16.2.0",
25-
"react-dom": "^16.2.0",
2626
"webpack": "^3.11.0"
2727
},
2828
"scripts": {

src/components/LazyLoadImage.jsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import ReactDOM from 'react-dom';
23
import { PropTypes } from 'prop-types';
34

45
class LazyLoadImage extends React.Component {
@@ -59,14 +60,19 @@ class LazyLoadImage extends React.Component {
5960
return false;
6061
}
6162

62-
getPlaceholderBoundingBox() {
63+
getPlaceholderBoundingBox(scrollPosition = this.props.scrollPosition) {
64+
const boundingRect = this.placeholder.getBoundingClientRect();
65+
const style = ReactDOM.findDOMNode(this.placeholder).style;
66+
const margin = {
67+
left: parseInt(style.getPropertyValue('margin-left'), 10) || 0,
68+
top: parseInt(style.getPropertyValue('margin-top'), 10) || 0
69+
};
70+
6371
return {
64-
bottom: this.placeholder.offsetTop +
65-
this.placeholder.offsetHeight,
66-
left: this.placeholder.offsetLeft,
67-
right: this.placeholder.offsetLeft +
68-
this.placeholder.offsetWidth,
69-
top: this.placeholder.offsetTop
72+
bottom: scrollPosition.y + boundingRect.bottom + margin.top,
73+
left: scrollPosition.x + boundingRect.left + margin.left,
74+
right: scrollPosition.x + boundingRect.right + margin.left,
75+
top: scrollPosition.y + boundingRect.top + margin.top
7076
};
7177
}
7278

@@ -105,7 +111,7 @@ class LazyLoadImage extends React.Component {
105111
}
106112

107113
const { threshold } = this.props;
108-
const boundingBox = this.getPlaceholderBoundingBox();
114+
const boundingBox = this.getPlaceholderBoundingBox(scrollPosition);
109115
const viewport = {
110116
bottom: scrollPosition.y + window.innerHeight,
111117
left: scrollPosition.x,
@@ -122,7 +128,7 @@ class LazyLoadImage extends React.Component {
122128
}
123129

124130
getPlaceholder() {
125-
const { className, height, placeholder, width } = this.props;
131+
const { className, height, placeholder, style, width } = this.props;
126132

127133
if (placeholder) {
128134
return React.cloneElement(placeholder,
@@ -132,7 +138,7 @@ class LazyLoadImage extends React.Component {
132138
return (
133139
<span className={'lazy-load-image-placeholder ' + className}
134140
ref={el => this.placeholder = el}
135-
style={{ height, width }}>
141+
style={{ height, width, ...style }}>
136142
</span>
137143
);
138144
}

src/components/LazyLoadImage.spec.js

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,39 @@ describe('LazyLoadImage', function() {
1414
afterLoad = () => null,
1515
beforeLoad = () => null,
1616
placeholder = null,
17-
scrollPosition = {x: 0, y: 0}
17+
scrollPosition = {x: 0, y: 0},
18+
style = {}
1819
} = {}) {
1920
return ReactTestUtils.renderIntoDocument(
2021
<LazyLoadImage
2122
afterLoad={afterLoad}
2223
beforeLoad={beforeLoad}
2324
placeholder={placeholder}
2425
scrollPosition={scrollPosition}
25-
src="" />
26+
src=""
27+
style={style} />
2628
);
2729
}
2830

31+
function simulateScroll(lazyLoadImage, offsetX = 0, offsetY = 0) {
32+
const myMock = jest.fn();
33+
34+
myMock.mockReturnValue({
35+
bottom: -offsetY,
36+
height: 0,
37+
left: -offsetX,
38+
right: -offsetX,
39+
top: -offsetY,
40+
width: 0
41+
});
42+
43+
lazyLoadImage.placeholder.getBoundingClientRect = myMock;
44+
45+
lazyLoadImage.componentWillReceiveProps({
46+
scrollPosition: {x: offsetX, y: offsetY}
47+
});
48+
}
49+
2950
function expectImages(wrapper, numberOfImages) {
3051
const img = scryRenderedDOMComponentsWithTag(wrapper, 'img');
3152

@@ -40,18 +61,21 @@ describe('LazyLoadImage', function() {
4061

4162
it('renders the default placeholder when it\'s not in the viewport', function() {
4263
const lazyLoadImage = renderLazyLoadImage({
43-
scrollPosition: {x: 0, y: -1000}
64+
style: {marginTop: 100000}
4465
});
4566

4667
expectImages(lazyLoadImage, 0);
4768
expectPlaceholders(lazyLoadImage, 1);
4869
});
4970

5071
it('renders the prop placeholder when it\'s not in the viewport', function() {
51-
const placeholder = <span className="test-placeholder"></span>;
72+
const style = {marginTop: 100000};
73+
const placeholder = (
74+
<span className="test-placeholder" style={style}></span>
75+
);
5276
const lazyLoadImage = renderLazyLoadImage({
53-
scrollPosition: {x: 0, y: -1000},
54-
placeholder
77+
placeholder,
78+
style
5579
});
5680

5781
expectImages(lazyLoadImage, 0);
@@ -66,22 +90,24 @@ describe('LazyLoadImage', function() {
6690
});
6791

6892
it('renders the image when it appears in the viewport', function() {
93+
const offset = 100000;
6994
const lazyLoadImage = renderLazyLoadImage({
70-
scrollPosition: {x: 0, y: -1000}
95+
style: {marginTop: offset}
7196
});
7297

73-
lazyLoadImage.componentWillReceiveProps({scrollPosition: {x: 0, y: 0}});
98+
simulateScroll(lazyLoadImage, 0, offset);
7499

75100
expectImages(lazyLoadImage, 1);
76101
expectPlaceholders(lazyLoadImage, 0);
77102
});
78103

79104
it('renders the image when it appears in the viewport horizontally', function() {
105+
const offset = 100000;
80106
const lazyLoadImage = renderLazyLoadImage({
81-
scrollPosition: {x: -1000, y: 0}
107+
style: {marginLeft: offset}
82108
});
83109

84-
lazyLoadImage.componentWillReceiveProps({scrollPosition: {x: 0, y: 0}});
110+
simulateScroll(lazyLoadImage, offset, 0);
85111

86112
expectImages(lazyLoadImage, 1);
87113
expectPlaceholders(lazyLoadImage, 0);
@@ -91,10 +117,10 @@ describe('LazyLoadImage', function() {
91117
const beforeLoad = jest.fn();
92118
const lazyLoadImage = renderLazyLoadImage({
93119
beforeLoad,
94-
scrollPosition: {x: 0, y: -1000}
120+
style: {marginTop: 100000}
95121
});
96122

97-
expect(beforeLoad).toHaveBeenCalledTimes(0);
123+
expect(beforeLoad).toHaveBeenCalledTimes(0);
98124
});
99125

100126
it('triggers beforeLoad when the image is in the viewport', function() {
@@ -108,12 +134,13 @@ describe('LazyLoadImage', function() {
108134

109135
it('triggers beforeLoad when the image appears in the viewport', function() {
110136
const beforeLoad = jest.fn();
137+
const offset = 100000;
111138
const lazyLoadImage = renderLazyLoadImage({
112139
beforeLoad,
113-
scrollPosition: {x: 0, y: -1000}
140+
style: {marginTop: offset}
114141
});
115142

116-
lazyLoadImage.componentWillReceiveProps({scrollPosition: {x: 0, y: 0}});
143+
simulateScroll(lazyLoadImage, 0, offset);
117144

118145
expect(beforeLoad).toHaveBeenCalledTimes(1);
119146
});
@@ -122,7 +149,7 @@ describe('LazyLoadImage', function() {
122149
const afterLoad = jest.fn();
123150
const lazyLoadImage = renderLazyLoadImage({
124151
afterLoad,
125-
scrollPosition: {x: 0, y: -1000}
152+
style: {marginTop: 100000}
126153
});
127154

128155
expect(afterLoad).toHaveBeenCalledTimes(0);
@@ -139,12 +166,13 @@ describe('LazyLoadImage', function() {
139166

140167
it('triggers afterLoad when the image appears in the viewport', function() {
141168
const afterLoad = jest.fn();
169+
const offset = 100000;
142170
const lazyLoadImage = renderLazyLoadImage({
143171
afterLoad,
144-
scrollPosition: {x: 0, y: -1000}
172+
style: {marginTop: offset}
145173
});
146174

147-
lazyLoadImage.componentWillReceiveProps({scrollPosition: {x: 0, y: 0}});
175+
simulateScroll(lazyLoadImage, 0, offset);
148176

149177
expect(afterLoad).toHaveBeenCalledTimes(1);
150178
});

0 commit comments

Comments
 (0)