Skip to content

pat-inject autoload-visible with IntersectionObserver #941

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

Merged
merged 10 commits into from
Mar 15, 2022
Merged
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
59 changes: 59 additions & 0 deletions src/core/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,63 @@ const create_from_string = (string) => {
return document.createRange().createContextualFragment(string.trim());
};

/**
* Return a CSS property value for a given DOM node.
* For length-values, relative values are converted to pixels.
* Optionally parse as pixels, if applicable.
*
* Note: The element must be attached to the body to make CSS caluclations work.
*
* @param {Node} el - DOM node.
* @param {String} property - CSS property to query on DOM node.
* @param {Boolean} [as_pixels=false] - Convert value to pixels, if applicable.
* @param {Boolean} [as_float=false] - Convert value to float, if applicable.
*
* @returns {(String|Number)} - The CSS value to return.
*/
function get_css_value(el, property, as_pixels = false, as_float = false) {
let value = window.getComputedStyle(el).getPropertyValue(property);
if (as_pixels || as_float) {
value = parseFloat(value) || 0.0;
}
if (as_pixels && !as_float) {
value = parseInt(Math.round(value), 10);
}
return value;
}

/**
* Find a scrollable element up in the DOM tree.
*
* Note: Setting the ``overflow`` shorthand property also sets the individual overflow-y and overflow-y properties.
*
* @param {Node} el - The DOM element to start the search on.
* @param {String} [direction=] - Not given: Search for any scrollable element up in the DOM tree.
* ``x``: Search for a horizontally scrollable element.
* ``y``: Search for a vertically scrollable element.
*
* @returns {Node} - Return the first scrollable element.
* If no other element could be found, document.body would be returned.
*/
const find_scroll_container = (el, direction) => {
while (el && el !== document.body) {
if (!direction || direction === "y") {
let overflow_y = get_css_value(el, "overflow-y");
if (["auto", "scroll"].includes(overflow_y)) {
return el;
}
}
if (!direction || direction === "x") {
let overflow_x = get_css_value(el, "overflow-x");
if (["auto", "scroll"].includes(overflow_x)) {
return el;
}
}
el = el.parentElement;
}
return el;
};

const dom = {
toNodeArray: toNodeArray,
querySelectorAllAndMe: querySelectorAllAndMe,
Expand All @@ -155,6 +212,8 @@ const dom = {
acquire_attribute: acquire_attribute,
is_visible: is_visible,
create_from_string: create_from_string,
get_css_value: get_css_value,
find_scroll_container: find_scroll_container,
add_event_listener: events.add_event_listener, // BBB export. TODO: Remove in an upcoming version.
remove_event_listener: events.remove_event_listener, // BBB export. TODO: Remove in an upcoming version.
};
Expand Down
199 changes: 199 additions & 0 deletions src/core/dom.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -484,4 +484,203 @@ describe("core.dom tests", () => {
done();
});
});

describe("get_css_value", function () {
beforeEach(function () {
document.body.innerHTML = `
<div
id="el1"
style="
position: relative;
margin-top: 1em;
font-size: 12px;
border: 1px solid black;
">
<div
id="el2"
style="
margin-bottom: 2em;
">
</div>
</div>
`;
});

afterEach(function () {
document.body.innerHTML = "";
});

it("Return values for CSS properties of a HTML node", function () {
const el1 = document.querySelector("#el1");
expect(dom.get_css_value(el1, "font-size")).toBe("12px");
expect(dom.get_css_value(el1, "font-size", true)).toBe(12);
expect(dom.get_css_value(el1, "position")).toBe("relative");
});

it("Return string, int or float, as requested.", function () {
const el1 = document.querySelector("#el1");
expect(dom.get_css_value(el1, "font-size")).toBe("12px");
expect(dom.get_css_value(el1, "font-size", true)).toBe(12);
expect(dom.get_css_value(el1, "font-size", true, true)).toBe(12.0);
expect(dom.get_css_value(el1, "font-size", null, true)).toBe(12.0);
});

it("Returns 0 for when requesting a numerical value which doesn't exist.", function () {
const el = document.createElement("div");
expect(dom.get_css_value(el, "hallo", true)).toBe(0);
expect(dom.get_css_value(el, "hallo", true, true)).toBe(0.0);
expect(dom.get_css_value(el, "hallo", null, true)).toBe(0.0);
});

it.skip("Return inherited values for CSS properties", function () {
// Missing JSDOM support for style inheritance yet. See:
// https://github.com/jsdom/jsdom/issues/2160
// https://github.com/jsdom/jsdom/pull/2668
// https://github.com/jsdom/jsdom/blob/master/Changelog.md

const el2 = document.querySelector("#el2");
expect(dom.get_css_value(el2, "font-size")).toBe("12px");
});

it.skip("Shorthand properties are split up", function () {
// Missing JSDOM support for property split yet.

const el1 = document.querySelector("#el1");
// ``em`` are parsed to pixel values.
// shorthand property sets like ``border`` are split up into their
// individual properties, like ``border-top-width``.
expect(dom.get_css_value(el1, "border-top-width")).toBe("12px");
expect(dom.get_css_value(el1, "border-top-style")).toBe("solid");
expect(dom.get_css_value(el1, "border-top-color")).toBe("rgb(0, 0, 0)");
});

it.skip("Values with relative units are converted to pixels", function () {
// Missing JSDOM support for unit conversion yet.

const el1 = document.querySelector("#el1");
const el2 = document.querySelector("#el2");
// Relative length-type values are converted to absolute pixels.
expect(dom.get_css_value(el1, "margin-top")).toBe("12px");
expect(dom.get_css_value(el1, "margin-top", true)).toBe(12);
expect(dom.get_css_value(el2, "margin-top", true)).toBe(0);
expect(dom.get_css_value(el2, "margin-bottom")).toBe("24px");
expect(dom.get_css_value(el2, "margin-bottom", true)).toBe(24);
});
});

describe("find_scroll_container", function () {
it("finds itself", function (done) {
document.body.innerHTML = `
<div
id="div1"
style="overflow-y: scroll">
<div
id="div2"
style="overflow-y: scroll">
</div>
</div>
`;
const div2 = document.querySelector("#div2");
expect(dom.find_scroll_container(div2)).toBe(div2);
done();
});
it("finds a scrollable parent", function (done) {
document.body.innerHTML = `
<div
id="div1"
style="overflow-y: scroll">
<div
id="div2">
</div>
</div>
`;
const div1 = document.querySelector("#div1");
const div2 = document.querySelector("#div2");
expect(dom.find_scroll_container(div2)).toBe(div1);
done();
});
it("finds any scrolling direction", function (done) {
document.body.innerHTML = `
<div
id="div1"
style="overflow-x: scroll">
<div
id="div2">
</div>
</div>
`;
const div1 = document.querySelector("#div1");
const div2 = document.querySelector("#div2");
expect(dom.find_scroll_container(div2)).toBe(div1);
done();
});
it.skip("finds any scrolling direction with generic overflow property", function (done) {
// Skipped due to jsDOM not setting overflow-x or overflow-y when only overflow is set.
document.body.innerHTML = `
<div
id="div1"
style="overflow: scroll">
<div
id="div2">
</div>
</div>
`;
const div1 = document.querySelector("#div1");
const div2 = document.querySelector("#div2");
expect(dom.find_scroll_container(div2)).toBe(div1);
done();
});
it("finds only scrolling direction y", function (done) {
document.body.innerHTML = `
<div
id="div1"
style="overflow-y: scroll">
<div
id="div2"
style="overflow-x: scroll">
<div
id="div3">
</div>
</div>
</div>
`;
const div1 = document.querySelector("#div1");
const div3 = document.querySelector("#div3");
expect(dom.find_scroll_container(div3, "y")).toBe(div1);
done();
});
it("finds only scrolling direction x", function (done) {
document.body.innerHTML = `
<div
id="div1"
style="overflow-x: scroll">
<div
id="div2"
style="overflow-y: scroll">
<div
id="div3">
</div>
</div>
</div>
`;
const div1 = document.querySelector("#div1");
const div3 = document.querySelector("#div3");
expect(dom.find_scroll_container(div3, "x")).toBe(div1);
done();
});
it("returns document.body if nothing else is found", function (done) {
document.body.innerHTML = `
<div
id="div1"
style="overflow-x: scroll">
<div
id="div2">
</div>
</div>
`;
const div2 = document.querySelector("#div2");
expect(dom.find_scroll_container(div2, "y")).toBe(document.body);
done();
});
});
});
17 changes: 1 addition & 16 deletions src/core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,21 +469,6 @@ function findRelatives(el) {
return $relatives;
}

function getCSSValue(el, property, as_pixels = false, as_float = false) {
/* Return a CSS property value for a given DOM node.
* For length-values, relative values are converted to pixels.
* Optionally parse as pixels, if applicable.
*/
let value = window.getComputedStyle(el).getPropertyValue(property);
if (as_pixels || as_float) {
value = parseFloat(value) || 0.0;
}
if (as_pixels && !as_float) {
value = parseInt(Math.round(value), 10);
}
return value;
}

function get_bounds(el) {
// Return bounds of an element with it's values rounded and converted to ints.
const bounds = el.getBoundingClientRect();
Expand Down Expand Up @@ -663,7 +648,6 @@ var utils = {
hasValue: hasValue,
parseTime: parseTime,
findRelatives: findRelatives,
getCSSValue: getCSSValue,
get_bounds: get_bounds,
checkInputSupport: checkInputSupport,
checkCSSFeature: checkCSSFeature,
Expand All @@ -676,6 +660,7 @@ var utils = {
localized_isodate: localized_isodate,
escape_html: escape_html,
unescape_html: unescape_html,
getCSSValue: dom.get_css_value, // BBB: moved to dom. TODO: Remove in upcoming version.
};

export default utils;
Loading