Skip to content

Commit 622d5e2

Browse files
committed
feat(core dom): Implement get_visible_ratio to calculate the visible ratio between an element and a container.
1 parent 15c6bf9 commit 622d5e2

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

src/core/dom.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,44 @@ const template = (template_string, template_variables = {}) => {
368368
return new Function("return `" + template_string + "`;").call(template_variables);
369369
};
370370

371+
/**
372+
* Get the visible ratio of an element compared to container.
373+
* If no container is given, the viewport is used.
374+
*
375+
* Note: currently only vertical ratio is supported.
376+
*
377+
* @param {Node} el - The element to get the visible ratio from.
378+
* @param {Node} [container] - The container to compare the element to.
379+
* @returns {number} - The visible ratio of the element.
380+
* 0 means the element is not visible.
381+
* 1 means the element is fully visible.
382+
*/
383+
const get_visible_ratio = (el, container) => {
384+
if (!el) {
385+
return 0;
386+
}
387+
388+
const rect = el.getBoundingClientRect();
389+
const container_rect =
390+
container !== window
391+
? container.getBoundingClientRect()
392+
: {
393+
top: 0,
394+
bottom: window.innerHeight,
395+
};
396+
397+
let visible_ratio = 0;
398+
if (rect.top < container_rect.bottom && rect.bottom > container_rect.top) {
399+
const rect_height = rect.bottom - rect.top;
400+
const visible_height =
401+
Math.min(rect.bottom, container_rect.bottom) -
402+
Math.max(rect.top, container_rect.top);
403+
visible_ratio = visible_height / rect_height;
404+
}
405+
406+
return visible_ratio;
407+
};
408+
371409
const dom = {
372410
toNodeArray: toNodeArray,
373411
querySelectorAllAndMe: querySelectorAllAndMe,
@@ -389,6 +427,7 @@ const dom = {
389427
set_data: set_data,
390428
delete_data: delete_data,
391429
template: template,
430+
get_visible_ratio: get_visible_ratio,
392431
add_event_listener: events.add_event_listener, // BBB export. TODO: Remove in an upcoming version.
393432
remove_event_listener: events.remove_event_listener, // BBB export. TODO: Remove in an upcoming version.
394433
};

src/core/dom.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,4 +806,53 @@ describe("core.dom tests", () => {
806806
done();
807807
});
808808
});
809+
810+
describe("get_visible_ratio", () => {
811+
it("returns 0 if the element is not given", (done) => {
812+
expect(dom.get_visible_ratio()).toBe(0);
813+
done();
814+
});
815+
816+
it("container = window, returns 0 if the element is not visible", (done) => {
817+
const el = document.createElement("div");
818+
jest.spyOn(el, "getBoundingClientRect").mockImplementation(() => {
819+
return {
820+
top: 200,
821+
bottom: 300,
822+
};
823+
});
824+
jest.replaceProperty(window, "innerHeight", 100);
825+
826+
expect(dom.get_visible_ratio(el, window)).toBe(0);
827+
done();
828+
});
829+
830+
it("container = window, returns 0.5 if the element is half-visible", (done) => {
831+
const el = document.createElement("div");
832+
jest.spyOn(el, "getBoundingClientRect").mockImplementation(() => {
833+
return {
834+
top: 50,
835+
bottom: 150,
836+
};
837+
});
838+
jest.replaceProperty(window, "innerHeight", 100);
839+
840+
expect(dom.get_visible_ratio(el, window)).toBe(0.5);
841+
done();
842+
});
843+
844+
it("container = window, returns 1 if the element is fully visible", (done) => {
845+
const el = document.createElement("div");
846+
jest.spyOn(el, "getBoundingClientRect").mockImplementation(() => {
847+
return {
848+
top: 0,
849+
bottom: 100,
850+
};
851+
});
852+
jest.replaceProperty(window, "innerHeight", 100);
853+
854+
expect(dom.get_visible_ratio(el, window)).toBe(1);
855+
done();
856+
});
857+
});
809858
});

0 commit comments

Comments
 (0)