intersection-observer 0.6.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -5
- package/intersection-observer-test.html +7 -0
- package/intersection-observer-test.js +1427 -3
- package/intersection-observer.js +317 -68
- package/package.json +1 -1
@@ -157,6 +157,21 @@ describe('IntersectionObserver', function() {
|
|
157
157
|
}).to.throwException();
|
158
158
|
});
|
159
159
|
|
160
|
+
it('fills in x and y in the resulting rects', function(done) {
|
161
|
+
io = new IntersectionObserver(function(records) {
|
162
|
+
expect(records.length).to.be(1);
|
163
|
+
var entry = records[0];
|
164
|
+
expect(entry.rootBounds.x).to.be(entry.rootBounds.left);
|
165
|
+
expect(entry.rootBounds.y).to.be(entry.rootBounds.top);
|
166
|
+
expect(entry.boundingClientRect.x).to.be(entry.boundingClientRect.left);
|
167
|
+
expect(entry.boundingClientRect.y).to.be(entry.boundingClientRect.top);
|
168
|
+
expect(entry.intersectionRect.x).to.be(entry.intersectionRect.left);
|
169
|
+
expect(entry.intersectionRect.y).to.be(entry.intersectionRect.top);
|
170
|
+
done();
|
171
|
+
}, {root: rootEl});
|
172
|
+
targetEl2.style.top = '-40px';
|
173
|
+
io.observe(targetEl1);
|
174
|
+
});
|
160
175
|
|
161
176
|
it('triggers for all targets when observing begins', function(done) {
|
162
177
|
io = new IntersectionObserver(function(records) {
|
@@ -941,6 +956,1413 @@ describe('IntersectionObserver', function() {
|
|
941
956
|
|
942
957
|
});
|
943
958
|
|
959
|
+
describe('iframe', function() {
|
960
|
+
var iframe;
|
961
|
+
var documentElement, body;
|
962
|
+
var iframeTargetEl1, iframeTargetEl2;
|
963
|
+
var bodyWidth;
|
964
|
+
|
965
|
+
beforeEach(function(done) {
|
966
|
+
iframe = document.createElement('iframe');
|
967
|
+
iframe.setAttribute('frameborder', '0');
|
968
|
+
iframe.setAttribute('scrolling', 'yes');
|
969
|
+
iframe.style.position = 'fixed';
|
970
|
+
iframe.style.top = '0px';
|
971
|
+
iframe.style.width = '100px';
|
972
|
+
iframe.style.height = '200px';
|
973
|
+
iframe.onerror = function() {
|
974
|
+
done(new Error('iframe initialization failed'));
|
975
|
+
};
|
976
|
+
iframe.onload = function() {
|
977
|
+
iframe.onload = null;
|
978
|
+
iframeWin = iframe.contentWindow;
|
979
|
+
iframeDoc = iframeWin.document;
|
980
|
+
iframeDoc.open();
|
981
|
+
iframeDoc.write('<!DOCTYPE html><html><body>');
|
982
|
+
iframeDoc.write('<style>');
|
983
|
+
iframeDoc.write('body {margin: 0}');
|
984
|
+
iframeDoc.write('.target {height: 200px; margin-bottom: 2px; background: blue;}');
|
985
|
+
iframeDoc.write('</style>');
|
986
|
+
iframeDoc.close();
|
987
|
+
|
988
|
+
// Ensure the documentElement and body are always sorted on top. See
|
989
|
+
// `sortRecords` for more info.
|
990
|
+
documentElement = iframeDoc.documentElement;
|
991
|
+
body = iframeDoc.body;
|
992
|
+
documentElement.id = 'A1';
|
993
|
+
body.id = 'A1';
|
994
|
+
|
995
|
+
function createTarget(id, bg) {
|
996
|
+
var target = iframeDoc.createElement('div');
|
997
|
+
target.id = id;
|
998
|
+
target.className = 'target';
|
999
|
+
target.style.background = bg;
|
1000
|
+
iframeDoc.body.appendChild(target);
|
1001
|
+
return target;
|
1002
|
+
}
|
1003
|
+
iframeTargetEl1 = createTarget('target1', 'blue');
|
1004
|
+
iframeTargetEl2 = createTarget('target2', 'green');
|
1005
|
+
bodyWidth = iframeDoc.body.clientWidth;
|
1006
|
+
done();
|
1007
|
+
};
|
1008
|
+
iframe.src = 'about:blank';
|
1009
|
+
rootEl.appendChild(iframe);
|
1010
|
+
});
|
1011
|
+
|
1012
|
+
afterEach(function() {
|
1013
|
+
rootEl.removeChild(iframe);
|
1014
|
+
});
|
1015
|
+
|
1016
|
+
function rect(r) {
|
1017
|
+
return {
|
1018
|
+
y: typeof r.y == 'number' ? r.y : r.top,
|
1019
|
+
x: typeof r.x == 'number' ? r.x : r.left,
|
1020
|
+
top: r.top,
|
1021
|
+
left: r.left,
|
1022
|
+
width: r.width != null ? r.width : r.right - r.left,
|
1023
|
+
height: r.height != null ? r.height : r.bottom - r.top,
|
1024
|
+
right: r.right != null ? r.right : r.left + r.width,
|
1025
|
+
bottom: r.bottom != null ? r.bottom : r.top + r.height
|
1026
|
+
};
|
1027
|
+
}
|
1028
|
+
|
1029
|
+
function getRootRect(doc) {
|
1030
|
+
var html = doc.documentElement;
|
1031
|
+
var body = doc.body;
|
1032
|
+
return rect({
|
1033
|
+
top: 0,
|
1034
|
+
left: 0,
|
1035
|
+
right: html.clientWidth || body.clientWidth,
|
1036
|
+
width: html.clientWidth || body.clientWidth,
|
1037
|
+
bottom: html.clientHeight || body.clientHeight,
|
1038
|
+
height: html.clientHeight || body.clientHeight
|
1039
|
+
});
|
1040
|
+
}
|
1041
|
+
|
1042
|
+
describe('same-origin iframe', function() {
|
1043
|
+
it('iframe targets do not intersect with a top root element', function(done) {
|
1044
|
+
var io = new IntersectionObserver(function(unsortedRecords) {
|
1045
|
+
var records = sortRecords(unsortedRecords);
|
1046
|
+
expect(records.length).to.be(2);
|
1047
|
+
expect(records[0].isIntersecting).to.be(false);
|
1048
|
+
expect(records[1].isIntersecting).to.be(false);
|
1049
|
+
done();
|
1050
|
+
io.disconnect();
|
1051
|
+
}, {root: rootEl});
|
1052
|
+
io.observe(iframeTargetEl1);
|
1053
|
+
io.observe(iframeTargetEl2);
|
1054
|
+
});
|
1055
|
+
|
1056
|
+
it('triggers for all targets in top-level root', function(done) {
|
1057
|
+
var io = new IntersectionObserver(function(unsortedRecords) {
|
1058
|
+
var records = sortRecords(unsortedRecords);
|
1059
|
+
expect(records.length).to.be(2);
|
1060
|
+
expect(records[0].isIntersecting).to.be(true);
|
1061
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1062
|
+
expect(records[1].isIntersecting).to.be(false);
|
1063
|
+
expect(records[1].intersectionRatio).to.be(0);
|
1064
|
+
|
1065
|
+
// The rootBounds is for the document's root.
|
1066
|
+
expect(records[0].rootBounds.height).to.be(innerHeight);
|
1067
|
+
|
1068
|
+
done();
|
1069
|
+
io.disconnect();
|
1070
|
+
});
|
1071
|
+
io.observe(iframeTargetEl1);
|
1072
|
+
io.observe(iframeTargetEl2);
|
1073
|
+
});
|
1074
|
+
|
1075
|
+
it('triggers for all targets in iframe-level root', function(done) {
|
1076
|
+
var io = new IntersectionObserver(function(unsortedRecords) {
|
1077
|
+
var records = sortRecords(unsortedRecords);
|
1078
|
+
expect(records.length).to.be(2);
|
1079
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1080
|
+
expect(records[1].intersectionRatio).to.be(1);
|
1081
|
+
|
1082
|
+
// The rootBounds is for the document's root.
|
1083
|
+
expect(rect(records[0].rootBounds)).
|
1084
|
+
to.eql(rect(iframeDoc.body.getBoundingClientRect()));
|
1085
|
+
|
1086
|
+
done();
|
1087
|
+
io.disconnect();
|
1088
|
+
}, {root: iframeDoc.body});
|
1089
|
+
io.observe(iframeTargetEl1);
|
1090
|
+
io.observe(iframeTargetEl2);
|
1091
|
+
});
|
1092
|
+
|
1093
|
+
it('calculates rects for a fully visible frame', function(done) {
|
1094
|
+
iframe.style.top = '0px';
|
1095
|
+
iframe.style.height = '300px';
|
1096
|
+
var io = new IntersectionObserver(function(unsortedRecords) {
|
1097
|
+
var records = sortRecords(unsortedRecords);
|
1098
|
+
expect(records.length).to.be(2);
|
1099
|
+
expect(rect(records[0].rootBounds)).to.eql(getRootRect(document));
|
1100
|
+
expect(rect(records[1].rootBounds)).to.eql(getRootRect(document));
|
1101
|
+
|
1102
|
+
// The target1 is fully visible.
|
1103
|
+
var clientRect1 = rect({
|
1104
|
+
top: 0,
|
1105
|
+
left: 0,
|
1106
|
+
width: bodyWidth,
|
1107
|
+
height: 200
|
1108
|
+
});
|
1109
|
+
expect(rect(records[0].boundingClientRect)).to.eql(clientRect1);
|
1110
|
+
expect(rect(records[0].intersectionRect)).to.eql(clientRect1);
|
1111
|
+
expect(records[0].isIntersecting).to.be(true);
|
1112
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1113
|
+
|
1114
|
+
// The target2 is partially visible.
|
1115
|
+
var clientRect2 = rect({
|
1116
|
+
top: 202,
|
1117
|
+
left: 0,
|
1118
|
+
width: bodyWidth,
|
1119
|
+
height: 200
|
1120
|
+
});
|
1121
|
+
var intersectRect2 = rect({
|
1122
|
+
top: 202,
|
1123
|
+
left: 0,
|
1124
|
+
width: bodyWidth,
|
1125
|
+
// The bottom is clipped off.
|
1126
|
+
bottom: 300
|
1127
|
+
});
|
1128
|
+
expect(rect(records[1].boundingClientRect)).to.eql(clientRect2);
|
1129
|
+
expect(rect(records[1].intersectionRect)).to.eql(intersectRect2);
|
1130
|
+
expect(records[1].isIntersecting).to.be(true);
|
1131
|
+
expect(records[1].intersectionRatio).to.be.within(0.48, 0.5); // ~0.5
|
1132
|
+
|
1133
|
+
done();
|
1134
|
+
io.disconnect();
|
1135
|
+
});
|
1136
|
+
io.observe(iframeTargetEl1);
|
1137
|
+
io.observe(iframeTargetEl2);
|
1138
|
+
});
|
1139
|
+
|
1140
|
+
it('calculates rects for a fully visible and offset frame', function(done) {
|
1141
|
+
iframe.style.top = '10px';
|
1142
|
+
iframe.style.height = '300px';
|
1143
|
+
var io = new IntersectionObserver(function(unsortedRecords) {
|
1144
|
+
var records = sortRecords(unsortedRecords);
|
1145
|
+
expect(records.length).to.be(2);
|
1146
|
+
expect(rect(records[0].rootBounds)).to.eql(getRootRect(document));
|
1147
|
+
expect(rect(records[1].rootBounds)).to.eql(getRootRect(document));
|
1148
|
+
|
1149
|
+
// The target1 is fully visible.
|
1150
|
+
var clientRect1 = rect({
|
1151
|
+
top: 0,
|
1152
|
+
left: 0,
|
1153
|
+
width: bodyWidth,
|
1154
|
+
height: 200
|
1155
|
+
});
|
1156
|
+
expect(rect(records[0].boundingClientRect)).to.eql(clientRect1);
|
1157
|
+
expect(rect(records[0].intersectionRect)).to.eql(clientRect1);
|
1158
|
+
expect(records[0].isIntersecting).to.be(true);
|
1159
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1160
|
+
|
1161
|
+
// The target2 is partially visible.
|
1162
|
+
var clientRect2 = rect({
|
1163
|
+
top: 202,
|
1164
|
+
left: 0,
|
1165
|
+
width: bodyWidth,
|
1166
|
+
height: 200
|
1167
|
+
});
|
1168
|
+
var intersectRect2 = rect({
|
1169
|
+
top: 202,
|
1170
|
+
left: 0,
|
1171
|
+
width: bodyWidth,
|
1172
|
+
// The bottom is clipped off.
|
1173
|
+
bottom: 300
|
1174
|
+
});
|
1175
|
+
expect(rect(records[1].boundingClientRect)).to.eql(clientRect2);
|
1176
|
+
expect(rect(records[1].intersectionRect)).to.eql(intersectRect2);
|
1177
|
+
expect(records[1].isIntersecting).to.be(true);
|
1178
|
+
expect(records[1].intersectionRatio).to.be.within(0.48, 0.5); // ~0.5
|
1179
|
+
|
1180
|
+
done();
|
1181
|
+
io.disconnect();
|
1182
|
+
});
|
1183
|
+
io.observe(iframeTargetEl1);
|
1184
|
+
io.observe(iframeTargetEl2);
|
1185
|
+
});
|
1186
|
+
|
1187
|
+
it('calculates rects for a clipped frame on top', function(done) {
|
1188
|
+
iframe.style.top = '-10px';
|
1189
|
+
iframe.style.height = '300px';
|
1190
|
+
var io = new IntersectionObserver(function(unsortedRecords) {
|
1191
|
+
var records = sortRecords(unsortedRecords);
|
1192
|
+
expect(records.length).to.be(2);
|
1193
|
+
expect(rect(records[0].rootBounds)).to.eql(getRootRect(document));
|
1194
|
+
expect(rect(records[1].rootBounds)).to.eql(getRootRect(document));
|
1195
|
+
|
1196
|
+
// The target1 is clipped at the top by the iframe's clipping.
|
1197
|
+
var clientRect1 = rect({
|
1198
|
+
top: 0,
|
1199
|
+
left: 0,
|
1200
|
+
width: bodyWidth,
|
1201
|
+
height: 200
|
1202
|
+
});
|
1203
|
+
var intersectRect1 = rect({
|
1204
|
+
left: 0,
|
1205
|
+
width: bodyWidth,
|
1206
|
+
// Top is clipped.
|
1207
|
+
top: 10,
|
1208
|
+
height: 200 - 10
|
1209
|
+
});
|
1210
|
+
expect(rect(records[0].boundingClientRect)).to.eql(clientRect1);
|
1211
|
+
expect(rect(records[0].intersectionRect)).to.eql(intersectRect1);
|
1212
|
+
expect(records[0].isIntersecting).to.be(true);
|
1213
|
+
expect(records[0].intersectionRatio).to.within(0.94, 0.96); // ~0.95
|
1214
|
+
|
1215
|
+
// The target2 is partially visible.
|
1216
|
+
var clientRect2 = rect({
|
1217
|
+
top: 202,
|
1218
|
+
left: 0,
|
1219
|
+
width: bodyWidth,
|
1220
|
+
height: 200
|
1221
|
+
});
|
1222
|
+
var intersectRect2 = rect({
|
1223
|
+
top: 202,
|
1224
|
+
left: 0,
|
1225
|
+
width: bodyWidth,
|
1226
|
+
// The bottom is clipped off.
|
1227
|
+
bottom: 300
|
1228
|
+
});
|
1229
|
+
expect(rect(records[1].boundingClientRect)).to.eql(clientRect2);
|
1230
|
+
expect(rect(records[1].intersectionRect)).to.eql(intersectRect2);
|
1231
|
+
expect(records[1].isIntersecting).to.be(true);
|
1232
|
+
expect(records[1].intersectionRatio).to.be.within(0.48, 0.5); // ~0.49
|
1233
|
+
|
1234
|
+
done();
|
1235
|
+
io.disconnect();
|
1236
|
+
});
|
1237
|
+
io.observe(iframeTargetEl1);
|
1238
|
+
io.observe(iframeTargetEl2);
|
1239
|
+
});
|
1240
|
+
|
1241
|
+
it('calculates rects for a clipped frame on bottom', function(done) {
|
1242
|
+
iframe.style.top = 'auto';
|
1243
|
+
iframe.style.bottom = '-10px';
|
1244
|
+
iframe.style.height = '300px';
|
1245
|
+
var io = new IntersectionObserver(function(unsortedRecords) {
|
1246
|
+
var records = sortRecords(unsortedRecords);
|
1247
|
+
expect(records.length).to.be(2);
|
1248
|
+
expect(rect(records[0].rootBounds)).to.eql(getRootRect(document));
|
1249
|
+
expect(rect(records[1].rootBounds)).to.eql(getRootRect(document));
|
1250
|
+
|
1251
|
+
// The target1 is clipped at the top by the iframe's clipping.
|
1252
|
+
var clientRect1 = rect({
|
1253
|
+
top: 0,
|
1254
|
+
left: 0,
|
1255
|
+
width: bodyWidth,
|
1256
|
+
height: 200
|
1257
|
+
});
|
1258
|
+
expect(rect(records[0].boundingClientRect)).to.eql(clientRect1);
|
1259
|
+
expect(rect(records[0].intersectionRect)).to.eql(clientRect1);
|
1260
|
+
expect(records[0].isIntersecting).to.be(true);
|
1261
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1262
|
+
|
1263
|
+
// The target2 is partially visible.
|
1264
|
+
var clientRect2 = rect({
|
1265
|
+
top: 202,
|
1266
|
+
left: 0,
|
1267
|
+
width: bodyWidth,
|
1268
|
+
height: 200
|
1269
|
+
});
|
1270
|
+
var intersectRect2 = rect({
|
1271
|
+
top: 202,
|
1272
|
+
left: 0,
|
1273
|
+
width: bodyWidth,
|
1274
|
+
// The bottom is clipped off.
|
1275
|
+
bottom: 300 - 10
|
1276
|
+
});
|
1277
|
+
expect(rect(records[1].boundingClientRect)).to.eql(clientRect2);
|
1278
|
+
expect(rect(records[1].intersectionRect)).to.eql(intersectRect2);
|
1279
|
+
expect(records[1].isIntersecting).to.be(true);
|
1280
|
+
expect(records[1].intersectionRatio).to.be.within(0.43, 0.45); // ~0.44
|
1281
|
+
|
1282
|
+
done();
|
1283
|
+
io.disconnect();
|
1284
|
+
});
|
1285
|
+
io.observe(iframeTargetEl1);
|
1286
|
+
io.observe(iframeTargetEl2);
|
1287
|
+
});
|
1288
|
+
|
1289
|
+
it('calculates rects for a fully visible frame and scrolled', function(done) {
|
1290
|
+
iframe.style.top = '0px';
|
1291
|
+
iframe.style.height = '300px';
|
1292
|
+
iframeWin.scrollTo(0, 10);
|
1293
|
+
var io = new IntersectionObserver(function(unsortedRecords) {
|
1294
|
+
var records = sortRecords(unsortedRecords);
|
1295
|
+
expect(records.length).to.be(2);
|
1296
|
+
expect(rect(records[0].rootBounds)).to.eql(getRootRect(document));
|
1297
|
+
expect(rect(records[1].rootBounds)).to.eql(getRootRect(document));
|
1298
|
+
|
1299
|
+
// The target1 is fully visible.
|
1300
|
+
var clientRect1 = rect({
|
1301
|
+
top: -10,
|
1302
|
+
left: 0,
|
1303
|
+
width: bodyWidth,
|
1304
|
+
height: 200
|
1305
|
+
});
|
1306
|
+
var intersectRect1 = rect({
|
1307
|
+
top: 0,
|
1308
|
+
left: 0,
|
1309
|
+
width: bodyWidth,
|
1310
|
+
// Height is only for the visible area.
|
1311
|
+
height: 200 - 10
|
1312
|
+
});
|
1313
|
+
expect(rect(records[0].boundingClientRect)).to.eql(clientRect1);
|
1314
|
+
expect(rect(records[0].intersectionRect)).to.eql(intersectRect1);
|
1315
|
+
expect(records[0].isIntersecting).to.be(true);
|
1316
|
+
expect(records[0].intersectionRatio).to.within(0.94, 0.96); // ~0.95
|
1317
|
+
|
1318
|
+
// The target2 is partially visible.
|
1319
|
+
var clientRect2 = rect({
|
1320
|
+
top: 202 - 10,
|
1321
|
+
left: 0,
|
1322
|
+
width: bodyWidth,
|
1323
|
+
height: 200
|
1324
|
+
});
|
1325
|
+
var intersectRect2 = rect({
|
1326
|
+
top: 202 - 10,
|
1327
|
+
left: 0,
|
1328
|
+
width: bodyWidth,
|
1329
|
+
// The bottom is clipped off.
|
1330
|
+
bottom: 300
|
1331
|
+
});
|
1332
|
+
expect(rect(records[1].boundingClientRect)).to.eql(clientRect2);
|
1333
|
+
expect(rect(records[1].intersectionRect)).to.eql(intersectRect2);
|
1334
|
+
expect(records[1].isIntersecting).to.be(true);
|
1335
|
+
expect(records[1].intersectionRatio).to.be.within(0.53, 0.55); // ~0.54
|
1336
|
+
|
1337
|
+
done();
|
1338
|
+
io.disconnect();
|
1339
|
+
});
|
1340
|
+
io.observe(iframeTargetEl1);
|
1341
|
+
io.observe(iframeTargetEl2);
|
1342
|
+
});
|
1343
|
+
|
1344
|
+
it('calculates rects for a clipped frame on top and scrolled', function(done) {
|
1345
|
+
iframe.style.top = '-10px';
|
1346
|
+
iframe.style.height = '300px';
|
1347
|
+
iframeWin.scrollTo(0, 10);
|
1348
|
+
var io = new IntersectionObserver(function(unsortedRecords) {
|
1349
|
+
var records = sortRecords(unsortedRecords);
|
1350
|
+
expect(records.length).to.be(2);
|
1351
|
+
expect(rect(records[0].rootBounds)).to.eql(getRootRect(document));
|
1352
|
+
expect(rect(records[1].rootBounds)).to.eql(getRootRect(document));
|
1353
|
+
|
1354
|
+
// The target1 is clipped at the top by the iframe's clipping.
|
1355
|
+
var clientRect1 = rect({
|
1356
|
+
top: -10,
|
1357
|
+
left: 0,
|
1358
|
+
width: bodyWidth,
|
1359
|
+
height: 200
|
1360
|
+
});
|
1361
|
+
var intersectRect1 = rect({
|
1362
|
+
left: 0,
|
1363
|
+
width: bodyWidth,
|
1364
|
+
// Top is clipped.
|
1365
|
+
top: 10,
|
1366
|
+
// The height is less by both: offset and scroll.
|
1367
|
+
height: 200 - 10 - 10
|
1368
|
+
});
|
1369
|
+
expect(rect(records[0].boundingClientRect)).to.eql(clientRect1);
|
1370
|
+
expect(rect(records[0].intersectionRect)).to.eql(intersectRect1);
|
1371
|
+
expect(records[0].isIntersecting).to.be(true);
|
1372
|
+
expect(records[0].intersectionRatio).to.within(0.89, 0.91); // ~0.9
|
1373
|
+
|
1374
|
+
// The target2 is partially visible.
|
1375
|
+
var clientRect2 = rect({
|
1376
|
+
top: 202 - 10,
|
1377
|
+
left: 0,
|
1378
|
+
width: bodyWidth,
|
1379
|
+
height: 200
|
1380
|
+
});
|
1381
|
+
var intersectRect2 = rect({
|
1382
|
+
top: 202 - 10,
|
1383
|
+
left: 0,
|
1384
|
+
width: bodyWidth,
|
1385
|
+
// The bottom is clipped off.
|
1386
|
+
bottom: 300
|
1387
|
+
});
|
1388
|
+
expect(rect(records[1].boundingClientRect)).to.eql(clientRect2);
|
1389
|
+
expect(rect(records[1].intersectionRect)).to.eql(intersectRect2);
|
1390
|
+
expect(records[1].isIntersecting).to.be(true);
|
1391
|
+
expect(records[1].intersectionRatio).to.be.within(0.53, 0.55); // ~0.54
|
1392
|
+
|
1393
|
+
done();
|
1394
|
+
io.disconnect();
|
1395
|
+
});
|
1396
|
+
io.observe(iframeTargetEl1);
|
1397
|
+
io.observe(iframeTargetEl2);
|
1398
|
+
});
|
1399
|
+
|
1400
|
+
it('handles style changes', function(done) {
|
1401
|
+
var spy = sinon.spy();
|
1402
|
+
|
1403
|
+
// When first element becomes invisible, the second element will show.
|
1404
|
+
// And in reverse: when the first element becomes visible again, the
|
1405
|
+
// second element will disappear.
|
1406
|
+
var io = new IntersectionObserver(spy);
|
1407
|
+
io.observe(iframeTargetEl1);
|
1408
|
+
io.observe(iframeTargetEl2);
|
1409
|
+
|
1410
|
+
runSequence([
|
1411
|
+
function(done) {
|
1412
|
+
setTimeout(function() {
|
1413
|
+
expect(spy.callCount).to.be(1);
|
1414
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
1415
|
+
expect(records.length).to.be(2);
|
1416
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1417
|
+
expect(records[0].isIntersecting).to.be(true);
|
1418
|
+
expect(records[1].intersectionRatio).to.be(0);
|
1419
|
+
expect(records[1].isIntersecting).to.be(false);
|
1420
|
+
done();
|
1421
|
+
}, ASYNC_TIMEOUT);
|
1422
|
+
},
|
1423
|
+
function(done) {
|
1424
|
+
iframeTargetEl1.style.display = 'none';
|
1425
|
+
setTimeout(function() {
|
1426
|
+
expect(spy.callCount).to.be(2);
|
1427
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
1428
|
+
expect(records.length).to.be(2);
|
1429
|
+
expect(records[0].intersectionRatio).to.be(0);
|
1430
|
+
expect(records[0].isIntersecting).to.be(false);
|
1431
|
+
expect(records[1].intersectionRatio).to.be(1);
|
1432
|
+
expect(records[1].isIntersecting).to.be(true);
|
1433
|
+
done();
|
1434
|
+
}, ASYNC_TIMEOUT);
|
1435
|
+
},
|
1436
|
+
function(done) {
|
1437
|
+
iframeTargetEl1.style.display = '';
|
1438
|
+
setTimeout(function() {
|
1439
|
+
expect(spy.callCount).to.be(3);
|
1440
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
1441
|
+
expect(records.length).to.be(2);
|
1442
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1443
|
+
expect(records[0].isIntersecting).to.be(true);
|
1444
|
+
expect(records[1].intersectionRatio).to.be(0);
|
1445
|
+
expect(records[1].isIntersecting).to.be(false);
|
1446
|
+
done();
|
1447
|
+
}, ASYNC_TIMEOUT);
|
1448
|
+
},
|
1449
|
+
function(done) {
|
1450
|
+
io.disconnect();
|
1451
|
+
done();
|
1452
|
+
}
|
1453
|
+
], done);
|
1454
|
+
});
|
1455
|
+
|
1456
|
+
it('handles scroll changes', function(done) {
|
1457
|
+
var spy = sinon.spy();
|
1458
|
+
|
1459
|
+
// Scrolling to the middle of the iframe shows the second box and
|
1460
|
+
// hides the first.
|
1461
|
+
var io = new IntersectionObserver(spy);
|
1462
|
+
io.observe(iframeTargetEl1);
|
1463
|
+
io.observe(iframeTargetEl2);
|
1464
|
+
|
1465
|
+
runSequence([
|
1466
|
+
function(done) {
|
1467
|
+
setTimeout(function() {
|
1468
|
+
expect(spy.callCount).to.be(1);
|
1469
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
1470
|
+
expect(records.length).to.be(2);
|
1471
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1472
|
+
expect(records[0].isIntersecting).to.be(true);
|
1473
|
+
expect(records[1].intersectionRatio).to.be(0);
|
1474
|
+
expect(records[1].isIntersecting).to.be(false);
|
1475
|
+
done();
|
1476
|
+
}, ASYNC_TIMEOUT);
|
1477
|
+
},
|
1478
|
+
function(done) {
|
1479
|
+
iframeWin.scrollTo(0, 202);
|
1480
|
+
setTimeout(function() {
|
1481
|
+
expect(spy.callCount).to.be(2);
|
1482
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
1483
|
+
expect(records.length).to.be(2);
|
1484
|
+
expect(records[0].intersectionRatio).to.be(0);
|
1485
|
+
expect(records[0].isIntersecting).to.be(false);
|
1486
|
+
expect(records[1].intersectionRatio).to.be(1);
|
1487
|
+
expect(records[1].isIntersecting).to.be(true);
|
1488
|
+
done();
|
1489
|
+
}, ASYNC_TIMEOUT);
|
1490
|
+
},
|
1491
|
+
function(done) {
|
1492
|
+
iframeWin.scrollTo(0, 0);
|
1493
|
+
setTimeout(function() {
|
1494
|
+
expect(spy.callCount).to.be(3);
|
1495
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
1496
|
+
expect(records.length).to.be(2);
|
1497
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1498
|
+
expect(records[0].isIntersecting).to.be(true);
|
1499
|
+
expect(records[1].intersectionRatio).to.be(0);
|
1500
|
+
expect(records[1].isIntersecting).to.be(false);
|
1501
|
+
done();
|
1502
|
+
}, ASYNC_TIMEOUT);
|
1503
|
+
},
|
1504
|
+
function(done) {
|
1505
|
+
io.disconnect();
|
1506
|
+
done();
|
1507
|
+
}
|
1508
|
+
], done);
|
1509
|
+
});
|
1510
|
+
|
1511
|
+
it('handles iframe changes', function(done) {
|
1512
|
+
var spy = sinon.spy();
|
1513
|
+
|
1514
|
+
// Iframe goes off screen and returns.
|
1515
|
+
var io = new IntersectionObserver(spy);
|
1516
|
+
io.observe(iframeTargetEl1);
|
1517
|
+
io.observe(iframeTargetEl2);
|
1518
|
+
|
1519
|
+
runSequence([
|
1520
|
+
function(done) {
|
1521
|
+
setTimeout(function() {
|
1522
|
+
expect(spy.callCount).to.be(1);
|
1523
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
1524
|
+
expect(records.length).to.be(2);
|
1525
|
+
expect(records[0].intersectionRatio).to.be(1);
|
1526
|
+
expect(records[0].isIntersecting).to.be(true);
|
1527
|
+
expect(records[1].intersectionRatio).to.be(0);
|
1528
|
+
expect(records[1].isIntersecting).to.be(false);
|
1529
|
+
// Top-level bounds.
|
1530
|
+
expect(records[0].rootBounds.height).to.be(innerHeight);
|
1531
|
+
expect(records[0].intersectionRect.height).to.be(200);
|
1532
|
+
done();
|
1533
|
+
}, ASYNC_TIMEOUT);
|
1534
|
+
},
|
1535
|
+
function(done) {
|
1536
|
+
// Completely off screen.
|
1537
|
+
iframe.style.top = '-202px';
|
1538
|
+
setTimeout(function() {
|
1539
|
+
expect(spy.callCount).to.be(2);
|
1540
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
1541
|
+
expect(records.length).to.be(1);
|
1542
|
+
expect(records[0].intersectionRatio).to.be(0);
|
1543
|
+
expect(records[0].isIntersecting).to.be(false);
|
1544
|
+
// Top-level bounds.
|
1545
|
+
expect(records[0].rootBounds.height).to.be(innerHeight);
|
1546
|
+
expect(records[0].intersectionRect.height).to.be(0);
|
1547
|
+
done();
|
1548
|
+
}, ASYNC_TIMEOUT);
|
1549
|
+
},
|
1550
|
+
function(done) {
|
1551
|
+
// Partially returns.
|
1552
|
+
iframe.style.top = '-100px';
|
1553
|
+
setTimeout(function() {
|
1554
|
+
expect(spy.callCount).to.be(3);
|
1555
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
1556
|
+
expect(records.length).to.be(1);
|
1557
|
+
expect(records[0].intersectionRatio).to.be.within(0.45, 0.55);
|
1558
|
+
expect(records[0].isIntersecting).to.be(true);
|
1559
|
+
// Top-level bounds.
|
1560
|
+
expect(records[0].rootBounds.height).to.be(innerHeight);
|
1561
|
+
expect(records[0].intersectionRect.height / 200).to.be.within(0.45, 0.55);
|
1562
|
+
done();
|
1563
|
+
}, ASYNC_TIMEOUT);
|
1564
|
+
},
|
1565
|
+
function(done) {
|
1566
|
+
io.disconnect();
|
1567
|
+
done();
|
1568
|
+
}
|
1569
|
+
], done);
|
1570
|
+
});
|
1571
|
+
|
1572
|
+
it('continues to monitor until the last target unobserved', function(done) {
|
1573
|
+
var spy = sinon.spy();
|
1574
|
+
|
1575
|
+
// Iframe goes off screen and returns.
|
1576
|
+
var io = new IntersectionObserver(spy);
|
1577
|
+
io.observe(target1);
|
1578
|
+
io.observe(iframeTargetEl1);
|
1579
|
+
io.observe(iframeTargetEl2);
|
1580
|
+
|
1581
|
+
runSequence([
|
1582
|
+
function(done) {
|
1583
|
+
setTimeout(function() {
|
1584
|
+
expect(spy.callCount).to.be(1);
|
1585
|
+
expect(spy.lastCall.args[0].length).to.be(3);
|
1586
|
+
|
1587
|
+
// Unobserve one from the main context and one from iframe.
|
1588
|
+
io.unobserve(target1);
|
1589
|
+
io.unobserve(iframeTargetEl2);
|
1590
|
+
|
1591
|
+
done();
|
1592
|
+
}, ASYNC_TIMEOUT);
|
1593
|
+
},
|
1594
|
+
function(done) {
|
1595
|
+
// Completely off screen.
|
1596
|
+
iframe.style.top = '-202px';
|
1597
|
+
setTimeout(function() {
|
1598
|
+
expect(spy.callCount).to.be(2);
|
1599
|
+
expect(spy.lastCall.args[0].length).to.be(1);
|
1600
|
+
|
1601
|
+
io.unobserve(iframeTargetEl1);
|
1602
|
+
|
1603
|
+
done();
|
1604
|
+
}, ASYNC_TIMEOUT);
|
1605
|
+
},
|
1606
|
+
function(done) {
|
1607
|
+
// Partially returns.
|
1608
|
+
iframe.style.top = '-100px';
|
1609
|
+
setTimeout(function() {
|
1610
|
+
expect(spy.callCount).to.be(2);
|
1611
|
+
done();
|
1612
|
+
}, ASYNC_TIMEOUT);
|
1613
|
+
},
|
1614
|
+
function(done) {
|
1615
|
+
io.disconnect();
|
1616
|
+
done();
|
1617
|
+
}
|
1618
|
+
], done);
|
1619
|
+
});
|
1620
|
+
});
|
1621
|
+
|
1622
|
+
describe('cross-origin iframe', function() {
|
1623
|
+
var ASYNC_TIMEOUT = 300;
|
1624
|
+
var crossOriginUpdater;
|
1625
|
+
|
1626
|
+
beforeEach(function(done) {
|
1627
|
+
Object.defineProperty(iframeWin, 'frameElement', {value: null});
|
1628
|
+
|
1629
|
+
/* Uncomment these lines to force polyfill inside the iframe.
|
1630
|
+
delete iframeWin.IntersectionObserver;
|
1631
|
+
delete iframeWin.IntersectionObserverEntry;
|
1632
|
+
*/
|
1633
|
+
|
1634
|
+
// Install polyfill right into the iframe.
|
1635
|
+
if (!iframeWin.IntersectionObserver) {
|
1636
|
+
var script = iframeDoc.createElement('script');
|
1637
|
+
script.src = 'intersection-observer.js';
|
1638
|
+
script.onload = function() {
|
1639
|
+
if (iframeWin.IntersectionObserver._setupCrossOriginUpdater) {
|
1640
|
+
crossOriginUpdater = iframeWin.IntersectionObserver._setupCrossOriginUpdater();
|
1641
|
+
}
|
1642
|
+
done();
|
1643
|
+
};
|
1644
|
+
iframeDoc.body.appendChild(script);
|
1645
|
+
} else {
|
1646
|
+
done();
|
1647
|
+
}
|
1648
|
+
});
|
1649
|
+
|
1650
|
+
afterEach(function() {
|
1651
|
+
if (IntersectionObserver._resetCrossOriginUpdater) {
|
1652
|
+
IntersectionObserver._resetCrossOriginUpdater();
|
1653
|
+
}
|
1654
|
+
});
|
1655
|
+
|
1656
|
+
function computeRectIntersection(rect1, rect2) {
|
1657
|
+
var top = Math.max(rect1.top, rect2.top);
|
1658
|
+
var bottom = Math.min(rect1.bottom, rect2.bottom);
|
1659
|
+
var left = Math.max(rect1.left, rect2.left);
|
1660
|
+
var right = Math.min(rect1.right, rect2.right);
|
1661
|
+
var width = right - left;
|
1662
|
+
var height = bottom - top;
|
1663
|
+
|
1664
|
+
return (width >= 0 && height >= 0) && {
|
1665
|
+
top: top,
|
1666
|
+
bottom: bottom,
|
1667
|
+
left: left,
|
1668
|
+
right: right,
|
1669
|
+
width: width,
|
1670
|
+
height: height
|
1671
|
+
} || {
|
1672
|
+
top: 0,
|
1673
|
+
bottom: 0,
|
1674
|
+
left: 0,
|
1675
|
+
right: 0,
|
1676
|
+
width: 0,
|
1677
|
+
height: 0
|
1678
|
+
};
|
1679
|
+
}
|
1680
|
+
|
1681
|
+
function checkRootBoundsAreNull(records) {
|
1682
|
+
if (!supportsNativeIntersectionObserver(iframeWin)) {
|
1683
|
+
records.forEach(function(record) {
|
1684
|
+
expect(record.rootBounds).to.be(null);
|
1685
|
+
});
|
1686
|
+
}
|
1687
|
+
}
|
1688
|
+
|
1689
|
+
function applyParentRect(parentRect) {
|
1690
|
+
if (crossOriginUpdater) {
|
1691
|
+
var parentIntersectionRect = computeRectIntersection(
|
1692
|
+
parentRect, getRootRect(document));
|
1693
|
+
crossOriginUpdater(parentRect, parentIntersectionRect);
|
1694
|
+
} else {
|
1695
|
+
iframe.style.top = parentRect.top + 'px';
|
1696
|
+
iframe.style.left = parentRect.left + 'px';
|
1697
|
+
iframe.style.height = parentRect.height + 'px';
|
1698
|
+
iframe.style.width = parentRect.width + 'px';
|
1699
|
+
}
|
1700
|
+
}
|
1701
|
+
|
1702
|
+
function createObserver(callback, options, parentRect) {
|
1703
|
+
var io = new iframeWin.IntersectionObserver(callback, options);
|
1704
|
+
if (parentRect) {
|
1705
|
+
applyParentRect(parentRect);
|
1706
|
+
}
|
1707
|
+
return io;
|
1708
|
+
}
|
1709
|
+
|
1710
|
+
it('calculates rects for a fully visible frame', function(done) {
|
1711
|
+
var parentRect = rect({top: 0, left: 20, height: 300, width: 100});
|
1712
|
+
var io = createObserver(function(unsortedRecords) {
|
1713
|
+
var records = sortRecords(unsortedRecords);
|
1714
|
+
expect(records.length).to.be(3);
|
1715
|
+
checkRootBoundsAreNull(records);
|
1716
|
+
|
1717
|
+
// The documentElement is partially visible.
|
1718
|
+
expect(rect(records[0].boundingClientRect))
|
1719
|
+
.to.eql(rect(documentElement.getBoundingClientRect()));
|
1720
|
+
expect(rect(records[0].intersectionRect)).to.eql(rect({
|
1721
|
+
top: 0,
|
1722
|
+
left: 0,
|
1723
|
+
width: bodyWidth,
|
1724
|
+
height: 300
|
1725
|
+
}));
|
1726
|
+
expect(records[0].isIntersecting).to.be(true);
|
1727
|
+
// 300 / 404 == ~0.743
|
1728
|
+
expect(records[0].intersectionRatio).to.be.within(0.74, 0.75);
|
1729
|
+
|
1730
|
+
// The document.body is partially visible.
|
1731
|
+
expect(rect(records[1].boundingClientRect))
|
1732
|
+
.to.eql(rect(body.getBoundingClientRect()));
|
1733
|
+
expect(rect(records[1].intersectionRect)).to.eql(rect({
|
1734
|
+
top: 0,
|
1735
|
+
left: 0,
|
1736
|
+
width: bodyWidth,
|
1737
|
+
height: 300
|
1738
|
+
}));
|
1739
|
+
expect(records[1].isIntersecting).to.be(true);
|
1740
|
+
// 300 / 402 == ~0.746
|
1741
|
+
expect(records[1].intersectionRatio).to.be.within(0.74, 0.75);
|
1742
|
+
|
1743
|
+
// The target1 is fully visible.
|
1744
|
+
var clientRect1 = rect({
|
1745
|
+
top: 0,
|
1746
|
+
left: 0,
|
1747
|
+
width: bodyWidth,
|
1748
|
+
height: 200
|
1749
|
+
});
|
1750
|
+
expect(rect(records[2].boundingClientRect)).to.eql(clientRect1);
|
1751
|
+
expect(rect(records[2].intersectionRect)).to.eql(clientRect1);
|
1752
|
+
expect(records[2].isIntersecting).to.be(true);
|
1753
|
+
expect(records[2].intersectionRatio).to.be(1);
|
1754
|
+
|
1755
|
+
done();
|
1756
|
+
io.disconnect();
|
1757
|
+
}, {}, parentRect);
|
1758
|
+
io.observe(documentElement);
|
1759
|
+
io.observe(body);
|
1760
|
+
io.observe(iframeTargetEl1);
|
1761
|
+
});
|
1762
|
+
|
1763
|
+
it('calculates rects for a fully visible and offset frame', function(done) {
|
1764
|
+
var parentRect = rect({top: 10, left: 20, height: 300, width: 100});
|
1765
|
+
var io = createObserver(function(unsortedRecords) {
|
1766
|
+
var records = sortRecords(unsortedRecords);
|
1767
|
+
expect(records.length).to.be(3);
|
1768
|
+
checkRootBoundsAreNull(records);
|
1769
|
+
|
1770
|
+
// The documentElement is partially visible.
|
1771
|
+
expect(rect(records[0].boundingClientRect))
|
1772
|
+
.to.eql(rect(documentElement.getBoundingClientRect()));
|
1773
|
+
expect(rect(records[0].intersectionRect)).to.eql(rect({
|
1774
|
+
top: 0,
|
1775
|
+
left: 0,
|
1776
|
+
width: bodyWidth,
|
1777
|
+
height: 300
|
1778
|
+
}));
|
1779
|
+
expect(records[0].isIntersecting).to.be(true);
|
1780
|
+
// 300 / 404 == ~0.743
|
1781
|
+
expect(records[0].intersectionRatio).to.be.within(0.74, 0.75);
|
1782
|
+
|
1783
|
+
// The document.body is partially visible.
|
1784
|
+
expect(rect(records[1].boundingClientRect))
|
1785
|
+
.to.eql(rect(body.getBoundingClientRect()));
|
1786
|
+
expect(rect(records[1].intersectionRect)).to.eql(rect({
|
1787
|
+
top: 0,
|
1788
|
+
left: 0,
|
1789
|
+
width: bodyWidth,
|
1790
|
+
height: 300
|
1791
|
+
}));
|
1792
|
+
expect(records[1].isIntersecting).to.be(true);
|
1793
|
+
// 300 / 402 == ~0.746
|
1794
|
+
expect(records[1].intersectionRatio).to.be.within(0.74, 0.75);
|
1795
|
+
|
1796
|
+
// The target1 is fully visible.
|
1797
|
+
var clientRect1 = rect({
|
1798
|
+
top: 0,
|
1799
|
+
left: 0,
|
1800
|
+
width: bodyWidth,
|
1801
|
+
height: 200
|
1802
|
+
});
|
1803
|
+
expect(rect(records[2].boundingClientRect)).to.eql(clientRect1);
|
1804
|
+
expect(rect(records[2].intersectionRect)).to.eql(clientRect1);
|
1805
|
+
expect(records[2].isIntersecting).to.be(true);
|
1806
|
+
expect(records[2].intersectionRatio).to.be(1);
|
1807
|
+
|
1808
|
+
done();
|
1809
|
+
io.disconnect();
|
1810
|
+
}, {}, parentRect);
|
1811
|
+
io.observe(documentElement);
|
1812
|
+
io.observe(body);
|
1813
|
+
io.observe(iframeTargetEl1);
|
1814
|
+
});
|
1815
|
+
|
1816
|
+
it('calculates rects for a clipped frame on top', function(done) {
|
1817
|
+
var parentRect = rect({top: -10, left: 20, height: 300, width: 100});
|
1818
|
+
var io = createObserver(function(unsortedRecords) {
|
1819
|
+
var records = sortRecords(unsortedRecords);
|
1820
|
+
expect(records.length).to.be(3);
|
1821
|
+
checkRootBoundsAreNull(records);
|
1822
|
+
|
1823
|
+
// The documentElement is partially visible.
|
1824
|
+
expect(rect(records[0].boundingClientRect))
|
1825
|
+
.to.eql(rect(documentElement.getBoundingClientRect()));
|
1826
|
+
expect(rect(records[0].intersectionRect)).to.eql(rect({
|
1827
|
+
top: 10,
|
1828
|
+
left: 0,
|
1829
|
+
width: bodyWidth,
|
1830
|
+
height: 300 - 10
|
1831
|
+
}));
|
1832
|
+
expect(records[0].isIntersecting).to.be(true);
|
1833
|
+
// (300 - 10) / 404 == ~0.717
|
1834
|
+
expect(records[0].intersectionRatio).to.be.within(0.71, 0.72);
|
1835
|
+
|
1836
|
+
// The document.body is partially visible.
|
1837
|
+
expect(rect(records[1].boundingClientRect))
|
1838
|
+
.to.eql(rect(body.getBoundingClientRect()));
|
1839
|
+
expect(rect(records[1].intersectionRect)).to.eql(rect({
|
1840
|
+
top: 10,
|
1841
|
+
left: 0,
|
1842
|
+
width: bodyWidth,
|
1843
|
+
height: 300 - 10
|
1844
|
+
}));
|
1845
|
+
expect(records[1].isIntersecting).to.be(true);
|
1846
|
+
// (300 - 10) / 402 == ~0.721
|
1847
|
+
expect(records[1].intersectionRatio).to.be.within(0.72, 0.73);
|
1848
|
+
|
1849
|
+
// The target1 is clipped at the top by the iframe's clipping.
|
1850
|
+
var clientRect1 = rect({
|
1851
|
+
top: 0,
|
1852
|
+
left: 0,
|
1853
|
+
width: bodyWidth,
|
1854
|
+
height: 200
|
1855
|
+
});
|
1856
|
+
var intersectRect1 = rect({
|
1857
|
+
left: 0,
|
1858
|
+
width: bodyWidth,
|
1859
|
+
// Top is clipped.
|
1860
|
+
top: 10,
|
1861
|
+
height: 200 - 10
|
1862
|
+
});
|
1863
|
+
expect(rect(records[2].boundingClientRect)).to.eql(clientRect1);
|
1864
|
+
expect(rect(records[2].intersectionRect)).to.eql(intersectRect1);
|
1865
|
+
expect(records[2].isIntersecting).to.be(true);
|
1866
|
+
expect(records[2].intersectionRatio).to.within(0.94, 0.96); // ~0.95
|
1867
|
+
|
1868
|
+
done();
|
1869
|
+
io.disconnect();
|
1870
|
+
}, {}, parentRect);
|
1871
|
+
io.observe(documentElement);
|
1872
|
+
io.observe(body);
|
1873
|
+
io.observe(iframeTargetEl1);
|
1874
|
+
});
|
1875
|
+
|
1876
|
+
it('calculates rects for a clipped frame on bottom', function(done) {
|
1877
|
+
var rootRect = getRootRect(document);
|
1878
|
+
var parentRect = rect({top: rootRect.bottom - 300 + 10, left: 20, height: 300, width: 100});
|
1879
|
+
var io = createObserver(function(unsortedRecords) {
|
1880
|
+
var records = sortRecords(unsortedRecords);
|
1881
|
+
expect(records.length).to.be(3);
|
1882
|
+
checkRootBoundsAreNull(records);
|
1883
|
+
|
1884
|
+
// The documentElement is partially visible.
|
1885
|
+
expect(rect(records[0].boundingClientRect))
|
1886
|
+
.to.eql(rect(documentElement.getBoundingClientRect()));
|
1887
|
+
expect(rect(records[0].intersectionRect)).to.eql(rect({
|
1888
|
+
top: 0,
|
1889
|
+
left: 0,
|
1890
|
+
width: bodyWidth,
|
1891
|
+
height: 300 - 10
|
1892
|
+
}));
|
1893
|
+
expect(records[0].isIntersecting).to.be(true);
|
1894
|
+
// (300 - 10) / 404 == ~0.717
|
1895
|
+
expect(records[0].intersectionRatio).to.be.within(0.71, 0.72);
|
1896
|
+
|
1897
|
+
// The document.body is partially visible.
|
1898
|
+
expect(rect(records[1].boundingClientRect))
|
1899
|
+
.to.eql(rect(body.getBoundingClientRect()));
|
1900
|
+
expect(rect(records[1].intersectionRect)).to.eql(rect({
|
1901
|
+
top: 0,
|
1902
|
+
left: 0,
|
1903
|
+
width: bodyWidth,
|
1904
|
+
height: 300 - 10
|
1905
|
+
}));
|
1906
|
+
expect(records[1].isIntersecting).to.be(true);
|
1907
|
+
// (300 - 10) / 402 == ~0.721
|
1908
|
+
expect(records[1].intersectionRatio).to.be.within(0.72, 0.73);
|
1909
|
+
|
1910
|
+
// The target1 is clipped at the top by the iframe's clipping.
|
1911
|
+
var clientRect1 = rect({
|
1912
|
+
top: 0,
|
1913
|
+
left: 0,
|
1914
|
+
width: bodyWidth,
|
1915
|
+
height: 200
|
1916
|
+
});
|
1917
|
+
expect(rect(records[2].boundingClientRect)).to.eql(clientRect1);
|
1918
|
+
expect(rect(records[2].intersectionRect)).to.eql(clientRect1);
|
1919
|
+
expect(records[2].isIntersecting).to.be(true);
|
1920
|
+
expect(records[2].intersectionRatio).to.be(1);
|
1921
|
+
|
1922
|
+
done();
|
1923
|
+
io.disconnect();
|
1924
|
+
}, {}, parentRect);
|
1925
|
+
io.observe(documentElement);
|
1926
|
+
io.observe(body);
|
1927
|
+
io.observe(iframeTargetEl1);
|
1928
|
+
});
|
1929
|
+
|
1930
|
+
it('calculates rects for a fully visible and scrolled frame', function(done) {
|
1931
|
+
iframeWin.scrollTo(0, 10);
|
1932
|
+
var parentRect = rect({top: 0, left: 20, height: 300, width: 100});
|
1933
|
+
var io = createObserver(function(unsortedRecords) {
|
1934
|
+
var records = sortRecords(unsortedRecords);
|
1935
|
+
expect(records.length).to.be(3);
|
1936
|
+
checkRootBoundsAreNull(records);
|
1937
|
+
|
1938
|
+
// The documentElement is partially visible.
|
1939
|
+
expect(rect(records[0].boundingClientRect))
|
1940
|
+
.to.eql(rect(documentElement.getBoundingClientRect()));
|
1941
|
+
expect(rect(records[0].intersectionRect)).to.eql(rect({
|
1942
|
+
top: 0,
|
1943
|
+
left: 0,
|
1944
|
+
width: bodyWidth,
|
1945
|
+
height: 300
|
1946
|
+
}));
|
1947
|
+
expect(records[0].isIntersecting).to.be(true);
|
1948
|
+
// 300 / 404 == ~0.743
|
1949
|
+
expect(records[0].intersectionRatio).to.be.within(0.74, 0.75);
|
1950
|
+
|
1951
|
+
// The document.body is partially visible.
|
1952
|
+
expect(rect(records[1].boundingClientRect))
|
1953
|
+
.to.eql(rect(body.getBoundingClientRect()));
|
1954
|
+
expect(rect(records[1].intersectionRect)).to.eql(rect({
|
1955
|
+
top: 0,
|
1956
|
+
left: 0,
|
1957
|
+
width: bodyWidth,
|
1958
|
+
height: 300
|
1959
|
+
}));
|
1960
|
+
expect(records[1].isIntersecting).to.be(true);
|
1961
|
+
// 300 / 402 == ~0.746
|
1962
|
+
expect(records[1].intersectionRatio).to.be.within(0.74, 0.75);
|
1963
|
+
|
1964
|
+
// The target1 is fully visible.
|
1965
|
+
var clientRect1 = rect({
|
1966
|
+
top: -10,
|
1967
|
+
left: 0,
|
1968
|
+
width: bodyWidth,
|
1969
|
+
height: 200
|
1970
|
+
});
|
1971
|
+
var intersectRect1 = rect({
|
1972
|
+
top: 0,
|
1973
|
+
left: 0,
|
1974
|
+
width: bodyWidth,
|
1975
|
+
// Height is only for the visible area.
|
1976
|
+
height: 200 - 10
|
1977
|
+
});
|
1978
|
+
expect(rect(records[2].boundingClientRect)).to.eql(clientRect1);
|
1979
|
+
expect(rect(records[2].intersectionRect)).to.eql(intersectRect1);
|
1980
|
+
expect(records[2].isIntersecting).to.be(true);
|
1981
|
+
expect(records[2].intersectionRatio).to.within(0.94, 0.96); // ~0.95
|
1982
|
+
|
1983
|
+
done();
|
1984
|
+
io.disconnect();
|
1985
|
+
}, {}, parentRect);
|
1986
|
+
io.observe(documentElement);
|
1987
|
+
io.observe(body);
|
1988
|
+
io.observe(iframeTargetEl1);
|
1989
|
+
});
|
1990
|
+
|
1991
|
+
it('calculates rects for a clipped frame on top and scrolled', function(done) {
|
1992
|
+
iframeWin.scrollTo(0, 10);
|
1993
|
+
var parentRect = rect({top: -10, left: 0, height: 300, width: 100});
|
1994
|
+
var io = createObserver(function(unsortedRecords) {
|
1995
|
+
var records = sortRecords(unsortedRecords);
|
1996
|
+
expect(records.length).to.be(4);
|
1997
|
+
checkRootBoundsAreNull(records);
|
1998
|
+
|
1999
|
+
// The documentElement is partially visible.
|
2000
|
+
expect(rect(records[0].boundingClientRect))
|
2001
|
+
.to.eql(rect(documentElement.getBoundingClientRect()));
|
2002
|
+
expect(rect(records[0].intersectionRect)).to.eql(rect({
|
2003
|
+
top: 10,
|
2004
|
+
left: 0,
|
2005
|
+
width: bodyWidth,
|
2006
|
+
height: 300 - 10
|
2007
|
+
}));
|
2008
|
+
expect(records[0].isIntersecting).to.be(true);
|
2009
|
+
// (300 - 10) / 404 == ~0.717
|
2010
|
+
expect(records[0].intersectionRatio).to.be.within(0.71, 0.72);
|
2011
|
+
|
2012
|
+
// The document.body is partially visible.
|
2013
|
+
expect(rect(records[1].boundingClientRect))
|
2014
|
+
.to.eql(rect(body.getBoundingClientRect()));
|
2015
|
+
expect(rect(records[1].intersectionRect)).to.eql(rect({
|
2016
|
+
top: 10,
|
2017
|
+
left: 0,
|
2018
|
+
width: bodyWidth,
|
2019
|
+
height: 300 - 10
|
2020
|
+
}));
|
2021
|
+
expect(records[1].isIntersecting).to.be(true);
|
2022
|
+
// (300 - 10) / 402 == ~0.721
|
2023
|
+
expect(records[1].intersectionRatio).to.be.within(0.72, 0.73);
|
2024
|
+
|
2025
|
+
// The target1 is clipped at the top by the iframe's clipping.
|
2026
|
+
var clientRect1 = rect({
|
2027
|
+
top: -10,
|
2028
|
+
left: 0,
|
2029
|
+
width: bodyWidth,
|
2030
|
+
height: 200
|
2031
|
+
});
|
2032
|
+
var intersectRect1 = rect({
|
2033
|
+
left: 0,
|
2034
|
+
width: bodyWidth,
|
2035
|
+
// Top is clipped.
|
2036
|
+
top: 10,
|
2037
|
+
// The height is less by both: offset and scroll.
|
2038
|
+
height: 200 - 10 - 10
|
2039
|
+
});
|
2040
|
+
expect(rect(records[2].boundingClientRect)).to.eql(clientRect1);
|
2041
|
+
expect(rect(records[2].intersectionRect)).to.eql(intersectRect1);
|
2042
|
+
expect(records[2].isIntersecting).to.be(true);
|
2043
|
+
expect(records[2].intersectionRatio).to.within(0.89, 0.91); // ~0.9
|
2044
|
+
|
2045
|
+
// The target2 is partially visible.
|
2046
|
+
var clientRect2 = rect({
|
2047
|
+
top: 202 - 10,
|
2048
|
+
left: 0,
|
2049
|
+
width: bodyWidth,
|
2050
|
+
height: 200
|
2051
|
+
});
|
2052
|
+
var intersectRect2 = rect({
|
2053
|
+
top: 202 - 10,
|
2054
|
+
left: 0,
|
2055
|
+
width: bodyWidth,
|
2056
|
+
// The bottom is clipped off.
|
2057
|
+
bottom: 300
|
2058
|
+
});
|
2059
|
+
expect(rect(records[3].boundingClientRect)).to.eql(clientRect2);
|
2060
|
+
expect(rect(records[3].intersectionRect)).to.eql(intersectRect2);
|
2061
|
+
expect(records[3].isIntersecting).to.be(true);
|
2062
|
+
expect(records[3].intersectionRatio).to.be.within(0.53, 0.55); // ~0.54
|
2063
|
+
|
2064
|
+
done();
|
2065
|
+
io.disconnect();
|
2066
|
+
}, {}, parentRect);
|
2067
|
+
io.observe(documentElement);
|
2068
|
+
io.observe(body);
|
2069
|
+
io.observe(iframeTargetEl1);
|
2070
|
+
io.observe(iframeTargetEl2);
|
2071
|
+
});
|
2072
|
+
|
2073
|
+
it('calculates rects for a fully clipped frame', function(done) {
|
2074
|
+
var parentRect = rect({top: -400, left: 20, height: 300, width: 100});
|
2075
|
+
var io = createObserver(function(unsortedRecords) {
|
2076
|
+
var records = sortRecords(unsortedRecords);
|
2077
|
+
expect(records.length).to.be(3);
|
2078
|
+
checkRootBoundsAreNull(records);
|
2079
|
+
|
2080
|
+
var emptyRect = rect({
|
2081
|
+
top: 0,
|
2082
|
+
left: 0,
|
2083
|
+
width: 0,
|
2084
|
+
height: 0
|
2085
|
+
});
|
2086
|
+
|
2087
|
+
// The documentElement is completely invisible.
|
2088
|
+
expect(rect(records[0].boundingClientRect))
|
2089
|
+
.to.eql(rect(documentElement.getBoundingClientRect()));
|
2090
|
+
expect(rect(records[0].intersectionRect)).to.eql(emptyRect);
|
2091
|
+
expect(records[0].isIntersecting).to.be(false);
|
2092
|
+
expect(records[0].intersectionRatio).to.be(0);
|
2093
|
+
|
2094
|
+
// The document.body is completely invisible.
|
2095
|
+
expect(rect(records[1].boundingClientRect))
|
2096
|
+
.to.eql(rect(body.getBoundingClientRect()));
|
2097
|
+
expect(rect(records[1].intersectionRect)).to.eql(emptyRect);
|
2098
|
+
expect(records[1].isIntersecting).to.be(false);
|
2099
|
+
expect(records[1].intersectionRatio).to.be(0);
|
2100
|
+
|
2101
|
+
// The target1 is completely invisible.
|
2102
|
+
var clientRect1 = rect({
|
2103
|
+
top: 0,
|
2104
|
+
left: 0,
|
2105
|
+
width: bodyWidth,
|
2106
|
+
height: 200
|
2107
|
+
});
|
2108
|
+
expect(rect(records[2].boundingClientRect)).to.eql(clientRect1);
|
2109
|
+
expect(rect(records[2].intersectionRect)).to.eql(emptyRect);
|
2110
|
+
expect(records[2].isIntersecting).to.be(false);
|
2111
|
+
expect(records[2].intersectionRatio).to.be(0);
|
2112
|
+
|
2113
|
+
done();
|
2114
|
+
io.disconnect();
|
2115
|
+
}, {}, parentRect);
|
2116
|
+
io.observe(documentElement);
|
2117
|
+
io.observe(body);
|
2118
|
+
io.observe(iframeTargetEl1);
|
2119
|
+
});
|
2120
|
+
|
2121
|
+
it('blocks until crossOriginUpdater is called first time', function(done) {
|
2122
|
+
if (supportsNativeIntersectionObserver(iframeWin)) {
|
2123
|
+
// Skip: not possible to emulate with the native observer.
|
2124
|
+
done();
|
2125
|
+
return;
|
2126
|
+
}
|
2127
|
+
|
2128
|
+
var spy = sinon.spy();
|
2129
|
+
|
2130
|
+
var parentRect = rect({top: 0, left: 20, height: 300, width: 100});
|
2131
|
+
|
2132
|
+
var io = createObserver(spy, {});
|
2133
|
+
io.observe(iframeTargetEl1);
|
2134
|
+
|
2135
|
+
runSequence([
|
2136
|
+
function(done) {
|
2137
|
+
setTimeout(function() {
|
2138
|
+
expect(spy.callCount).to.be(0);
|
2139
|
+
|
2140
|
+
// Issue the first update.
|
2141
|
+
crossOriginUpdater(parentRect, null);
|
2142
|
+
|
2143
|
+
done();
|
2144
|
+
}, ASYNC_TIMEOUT);
|
2145
|
+
},
|
2146
|
+
function(done) {
|
2147
|
+
setTimeout(function() {
|
2148
|
+
expect(spy.callCount).to.be(1);
|
2149
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2150
|
+
expect(records.length).to.be(1);
|
2151
|
+
expect(records[0].intersectionRatio).to.be(0);
|
2152
|
+
expect(records[0].isIntersecting).to.be(false);
|
2153
|
+
done();
|
2154
|
+
}, ASYNC_TIMEOUT);
|
2155
|
+
},
|
2156
|
+
function(done) {
|
2157
|
+
io.disconnect();
|
2158
|
+
done();
|
2159
|
+
}
|
2160
|
+
], done);
|
2161
|
+
});
|
2162
|
+
|
2163
|
+
it('doesn\'t block with a root specified', function(done) {
|
2164
|
+
var spy = sinon.spy();
|
2165
|
+
|
2166
|
+
var io = createObserver(spy, {root: body});
|
2167
|
+
io.observe(iframeTargetEl1);
|
2168
|
+
|
2169
|
+
runSequence([
|
2170
|
+
function(done) {
|
2171
|
+
setTimeout(function() {
|
2172
|
+
expect(spy.callCount).to.be(1);
|
2173
|
+
var record = sortRecords(spy.lastCall.args[0])[0];
|
2174
|
+
expect(record.intersectionRatio).to.be(1);
|
2175
|
+
expect(record.isIntersecting).to.be(true);
|
2176
|
+
expect(rect(record.rootBounds)).to.eql(rect(body.getBoundingClientRect()));
|
2177
|
+
done();
|
2178
|
+
}, ASYNC_TIMEOUT);
|
2179
|
+
},
|
2180
|
+
function(done) {
|
2181
|
+
io.disconnect();
|
2182
|
+
done();
|
2183
|
+
}
|
2184
|
+
], done);
|
2185
|
+
});
|
2186
|
+
|
2187
|
+
it('handles style changes', function(done) {
|
2188
|
+
var spy = sinon.spy();
|
2189
|
+
|
2190
|
+
var parentRect = rect({top: 0, left: 0, height: 200, width: 100});
|
2191
|
+
|
2192
|
+
// When first element becomes invisible, the second element will show.
|
2193
|
+
// And in reverse: when the first element becomes visible again, the
|
2194
|
+
// second element will disappear.
|
2195
|
+
var io = createObserver(spy, {}, parentRect);
|
2196
|
+
io.observe(iframeTargetEl1);
|
2197
|
+
io.observe(iframeTargetEl2);
|
2198
|
+
|
2199
|
+
runSequence([
|
2200
|
+
function(done) {
|
2201
|
+
setTimeout(function() {
|
2202
|
+
expect(spy.callCount).to.be(1);
|
2203
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2204
|
+
expect(records.length).to.be(2);
|
2205
|
+
expect(records[0].intersectionRatio).to.be(1);
|
2206
|
+
expect(records[0].isIntersecting).to.be(true);
|
2207
|
+
expect(records[1].intersectionRatio).to.be(0);
|
2208
|
+
expect(records[1].isIntersecting).to.be(false);
|
2209
|
+
done();
|
2210
|
+
}, ASYNC_TIMEOUT);
|
2211
|
+
},
|
2212
|
+
function(done) {
|
2213
|
+
iframeTargetEl1.style.display = 'none';
|
2214
|
+
setTimeout(function() {
|
2215
|
+
expect(spy.callCount).to.be(2);
|
2216
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2217
|
+
expect(records.length).to.be(2);
|
2218
|
+
expect(records[0].intersectionRatio).to.be(0);
|
2219
|
+
expect(records[0].isIntersecting).to.be(false);
|
2220
|
+
expect(records[1].intersectionRatio).to.be(1);
|
2221
|
+
expect(records[1].isIntersecting).to.be(true);
|
2222
|
+
done();
|
2223
|
+
}, ASYNC_TIMEOUT);
|
2224
|
+
},
|
2225
|
+
function(done) {
|
2226
|
+
iframeTargetEl1.style.display = '';
|
2227
|
+
setTimeout(function() {
|
2228
|
+
expect(spy.callCount).to.be(3);
|
2229
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2230
|
+
expect(records.length).to.be(2);
|
2231
|
+
expect(records[0].intersectionRatio).to.be(1);
|
2232
|
+
expect(records[0].isIntersecting).to.be(true);
|
2233
|
+
expect(records[1].intersectionRatio).to.be(0);
|
2234
|
+
expect(records[1].isIntersecting).to.be(false);
|
2235
|
+
done();
|
2236
|
+
}, ASYNC_TIMEOUT);
|
2237
|
+
},
|
2238
|
+
function(done) {
|
2239
|
+
io.disconnect();
|
2240
|
+
done();
|
2241
|
+
}
|
2242
|
+
], done);
|
2243
|
+
});
|
2244
|
+
|
2245
|
+
it('handles scroll changes', function(done) {
|
2246
|
+
var spy = sinon.spy();
|
2247
|
+
|
2248
|
+
var parentRect = rect({top: 0, left: 0, height: 200, width: 100});
|
2249
|
+
|
2250
|
+
// Scrolling to the middle of the iframe shows the second box and
|
2251
|
+
// hides the first.
|
2252
|
+
var io = createObserver(spy, {}, parentRect);
|
2253
|
+
io.observe(iframeTargetEl1);
|
2254
|
+
io.observe(iframeTargetEl2);
|
2255
|
+
|
2256
|
+
runSequence([
|
2257
|
+
function(done) {
|
2258
|
+
setTimeout(function() {
|
2259
|
+
expect(spy.callCount).to.be(1);
|
2260
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2261
|
+
expect(records.length).to.be(2);
|
2262
|
+
expect(records[0].intersectionRatio).to.be(1);
|
2263
|
+
expect(records[0].isIntersecting).to.be(true);
|
2264
|
+
expect(records[1].intersectionRatio).to.be(0);
|
2265
|
+
expect(records[1].isIntersecting).to.be(false);
|
2266
|
+
done();
|
2267
|
+
}, ASYNC_TIMEOUT);
|
2268
|
+
},
|
2269
|
+
function(done) {
|
2270
|
+
iframeWin.scrollTo(0, 202);
|
2271
|
+
setTimeout(function() {
|
2272
|
+
expect(spy.callCount).to.be(2);
|
2273
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2274
|
+
expect(records.length).to.be(2);
|
2275
|
+
expect(records[0].intersectionRatio).to.be(0);
|
2276
|
+
expect(records[0].isIntersecting).to.be(false);
|
2277
|
+
expect(records[1].intersectionRatio).to.be(1);
|
2278
|
+
expect(records[1].isIntersecting).to.be(true);
|
2279
|
+
done();
|
2280
|
+
}, ASYNC_TIMEOUT);
|
2281
|
+
},
|
2282
|
+
function(done) {
|
2283
|
+
iframeWin.scrollTo(0, 0);
|
2284
|
+
setTimeout(function() {
|
2285
|
+
expect(spy.callCount).to.be(3);
|
2286
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2287
|
+
expect(records.length).to.be(2);
|
2288
|
+
expect(records[0].intersectionRatio).to.be(1);
|
2289
|
+
expect(records[0].isIntersecting).to.be(true);
|
2290
|
+
expect(records[1].intersectionRatio).to.be(0);
|
2291
|
+
expect(records[1].isIntersecting).to.be(false);
|
2292
|
+
done();
|
2293
|
+
}, ASYNC_TIMEOUT);
|
2294
|
+
},
|
2295
|
+
function(done) {
|
2296
|
+
io.disconnect();
|
2297
|
+
done();
|
2298
|
+
}
|
2299
|
+
], done);
|
2300
|
+
});
|
2301
|
+
|
2302
|
+
it('handles parent rect changes', function(done) {
|
2303
|
+
var spy = sinon.spy();
|
2304
|
+
|
2305
|
+
var parentRect = rect({top: 0, left: 0, height: 200, width: 100});
|
2306
|
+
|
2307
|
+
// Iframe goes off screen and returns.
|
2308
|
+
var io = createObserver(spy, {}, parentRect);
|
2309
|
+
io.observe(iframeTargetEl1);
|
2310
|
+
io.observe(iframeTargetEl2);
|
2311
|
+
|
2312
|
+
runSequence([
|
2313
|
+
function(done) {
|
2314
|
+
setTimeout(function() {
|
2315
|
+
expect(spy.callCount).to.be(1);
|
2316
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2317
|
+
expect(records.length).to.be(2);
|
2318
|
+
checkRootBoundsAreNull(records);
|
2319
|
+
expect(records[0].intersectionRatio).to.be(1);
|
2320
|
+
expect(records[0].isIntersecting).to.be(true);
|
2321
|
+
expect(records[1].intersectionRatio).to.be(0);
|
2322
|
+
expect(records[1].isIntersecting).to.be(false);
|
2323
|
+
// Top-level bounds.
|
2324
|
+
expect(records[0].intersectionRect.height).to.be(200);
|
2325
|
+
done();
|
2326
|
+
}, ASYNC_TIMEOUT);
|
2327
|
+
},
|
2328
|
+
function(done) {
|
2329
|
+
// Completely off screen.
|
2330
|
+
applyParentRect(rect({top: -202, left: 0, height: 200, width: 100}));
|
2331
|
+
setTimeout(function() {
|
2332
|
+
expect(spy.callCount).to.be(2);
|
2333
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2334
|
+
expect(records.length).to.be(1);
|
2335
|
+
checkRootBoundsAreNull(records);
|
2336
|
+
expect(records[0].intersectionRatio).to.be(0);
|
2337
|
+
expect(records[0].isIntersecting).to.be(false);
|
2338
|
+
// Top-level bounds.
|
2339
|
+
expect(records[0].intersectionRect.height).to.be(0);
|
2340
|
+
done();
|
2341
|
+
}, ASYNC_TIMEOUT);
|
2342
|
+
},
|
2343
|
+
function(done) {
|
2344
|
+
// Partially returns.
|
2345
|
+
applyParentRect(rect({top: -100, left: 0, height: 200, width: 100}));
|
2346
|
+
setTimeout(function() {
|
2347
|
+
expect(spy.callCount).to.be(3);
|
2348
|
+
var records = sortRecords(spy.lastCall.args[0]);
|
2349
|
+
expect(records.length).to.be(1);
|
2350
|
+
checkRootBoundsAreNull(records);
|
2351
|
+
expect(records[0].intersectionRatio).to.be.within(0.45, 0.55);
|
2352
|
+
expect(records[0].isIntersecting).to.be(true);
|
2353
|
+
// Top-level bounds.
|
2354
|
+
expect(records[0].intersectionRect.height / 200).to.be.within(0.45, 0.55);
|
2355
|
+
done();
|
2356
|
+
}, ASYNC_TIMEOUT);
|
2357
|
+
},
|
2358
|
+
function(done) {
|
2359
|
+
io.disconnect();
|
2360
|
+
done();
|
2361
|
+
}
|
2362
|
+
], done);
|
2363
|
+
});
|
2364
|
+
});
|
2365
|
+
});
|
944
2366
|
});
|
945
2367
|
|
946
2368
|
|
@@ -967,11 +2389,13 @@ function runSequence(functions, done) {
|
|
967
2389
|
/**
|
968
2390
|
* Returns whether or not the current browser has native support for
|
969
2391
|
* IntersectionObserver.
|
2392
|
+
* @param {Window=} win
|
970
2393
|
* @return {boolean} True if native support is detected.
|
971
2394
|
*/
|
972
|
-
function supportsNativeIntersectionObserver() {
|
973
|
-
|
974
|
-
|
2395
|
+
function supportsNativeIntersectionObserver(win) {
|
2396
|
+
win = win || window;
|
2397
|
+
return 'IntersectionObserver' in win &&
|
2398
|
+
win.IntersectionObserver.toString().indexOf('[native code]') > -1;
|
975
2399
|
}
|
976
2400
|
|
977
2401
|
|