I dont think there is any official polyfill for those functions in Firefox right now unfortunately.
I gave this a shot to see if there was a way to easily polyfill those functions with available API.
I, intentionaly, assigned a second function so we can compare the original supported function (when using a browser that natively support it like Chrome) with the polyfill.
So, here we are for getIntersectionList
:
const getIntersectionListPolyfill = function(rect, referenceElement) {
var intersectionList = [];
var root = this.ownerSVGElement || this;
// Get all elements that intersect with rect
var elements = root.querySelectorAll('*');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element !== this && element instanceof SVGGraphicsElement) {
var bbox = element.getBBox();
if (rect.width && rect.height && bbox.width && bbox.height) {
if (bbox.x + bbox.width > rect.x &&
bbox.y + bbox.height > rect.y &&
bbox.x < rect.x + rect.width &&
bbox.y < rect.y + rect.height) {
intersectionList.push(element);
}
}
}
}
// Sort elements in document order
intersectionList.sort(function(a, b) {
return (a.compareDocumentPosition(b) & 2) ? 1 : -1;
});
// Filter elements by referenceElement
if (referenceElement) {
intersectionList = intersectionList.filter(function(element) {
return element === referenceElement || element.contains(referenceElement);
});
}
return intersectionList;
}
if (!SVGElement.prototype.getIntersectionList) {
SVGElement.prototype.getIntersectionList = getIntersectionListPolyfill;
}
// The code below is for the snippet only
SVGElement.prototype.getIntersectionList2 = getIntersectionListPolyfill;
const mySVG = document.getElementById('mySVG');
const myRect = mySVG.createSVGRect();
myRect.width = myRect.height = 1;
myRect.x = myRect.y = 20;
console.log('Original', mySVG.getIntersectionList(myRect, null).length);
console.log('Polyfill', mySVG.getIntersectionList2(myRect, null).length);
svg {
display: block;
border: 1px solid #000;
margin: 20px 0;
visibility: visible;
}
rect, circle {
fill: rgba(255, 0, 0, 0.2);
visibility: visiblePainted;
}
<svg id="mySVG" width="500" height="400">
<rect x="10" y="10" width="200" height="100"></rect>
<rect x="20" y="20" width="200" height="100"></rect>
<circle cx="70" cy="70" r="50"></circle>
</svg>
This polyfill extends the SVGElement
prototype with a getIntersectionList
function that imit the native implementation. It uses querySelectorAll
to get every elements of the SVG and checks if they intersect with the given rect. It then sorts the elements in document order and filters them by the reference element, if provided.
getEnclosureList
is pretty similar:
const getEnclosureListPolyfill = function(rect, referenceElement) {
var enclosureList = [];
var root = this.ownerSVGElement || this;
// Get all elements that are completely enclosed by rect
var elements = root.querySelectorAll('*');
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element !== this && element instanceof SVGGraphicsElement) {
var bbox = element.getBBox();
if (rect.width && rect.height && bbox.width && bbox.height) {
if (bbox.x >= rect.x &&
bbox.y >= rect.y &&
bbox.x + bbox.width <= rect.x + rect.width &&
bbox.y + bbox.height <= rect.y + rect.height) {
enclosureList.push(element);
}
}
}
}
// Sort elements in document order
enclosureList.sort(function(a, b) {
return (a.compareDocumentPosition(b) & 2) ? 1 : -1;
});
// Filter elements by referenceElement
if (referenceElement) {
enclosureList = enclosureList.filter(function(element) {
return element === referenceElement || element.contains(referenceElement);
});
}
return enclosureList;
};
if (!SVGElement.prototype.getEnclosureList) {
SVGElement.prototype.getEnclosureList = getEnclosureListPolyfill;
}
// The code below is for the snippet only
SVGElement.prototype.getEnclosureList2 = getEnclosureListPolyfill;
const mySVG = document.getElementById('mySVG');
const myRect = mySVG.createSVGRect();
myRect.width = myRect.height = 210;
myRect.x = myRect.y = 0;
console.log('Original', mySVG.getEnclosureList(myRect, null).length);
console.log('Polyfill', mySVG.getEnclosureList2(myRect, null).length);
svg {
display: block;
border: 1px solid #000;
margin: 20px 0;
visibility: visible;
}
rect, circle {
fill: rgba(255, 0, 0, 0.2);
visibility: visiblePainted;
}
<svg id="mySVG" width="500" height="400">
<rect x="10" y="10" width="200" height="100"></rect>
<rect x="20" y="20" width="200" height="100"></rect>
<circle cx="70" cy="70" r="50"></circle>
</svg>
This polyfill extends the SVGElement
prototype with a getEnclosureList
function that imit the native implementation. It uses querySelectorAll
to get every elements of the SVG and checks if they completely enclosed by the given rect. It then sorts the elements in document order and filters them by the reference element, if provided.
checkIntersection
now:
const checkIntersectionPolyfill = function(element, rect) {
var root = this.ownerSVGElement || this;
// Get the bounding boxes of the two elements
var bbox1 = element.getBBox();
var bbox2 = rect;
// Check if the two bounding boxes intersect
if (bbox1.x + bbox1.width > bbox2.x &&
bbox1.y + bbox1.height > bbox2.y &&
bbox2.x + bbox2.width > bbox1.x &&
bbox2.y + bbox2.height > bbox1.y) {
// Check if the two elements actually intersect
var intersection = root.createSVGRect();
intersection.x = Math.max(bbox1.x, bbox2.x);
intersection.y = Math.max(bbox1.y, bbox2.y);
intersection.width = Math.min(bbox1.x + bbox1.width, bbox2.x + bbox2.width) - intersection.x;
intersection.height = Math.min(bbox1.y + bbox1.height, bbox2.y + bbox2.height) - intersection.y;
return intersection.width > 0 && intersection.height > 0;
} else {
return false;
}
};
if (!SVGElement.prototype.checkIntersection) {
SVGElement.prototype.checkIntersection = checkIntersectionPolyfill;
}
// The code below is for the snippet only
SVGElement.prototype.checkIntersection2 = checkIntersectionPolyfill;
const mySVG = document.getElementById('mySVG');
const myRect1 = document.getElementById('myRect1');
const myRect2 = mySVG.createSVGRect();
myRect2.width = myRect2.height = 100;
myRect2.x = myRect2.y = 0;
console.log('Original', mySVG.checkIntersection(myRect1, myRect2));
console.log('Polyfill', mySVG.checkIntersection2(myRect1, myRect2));
svg {
display: block;
border: 1px solid #000;
margin: 20px 0;
visibility: visible;
}
rect, circle {
fill: rgba(255, 0, 0, 0.2);
visibility: visiblePainted;
}
<svg id="mySVG" width="500" height="400">
<rect id="myRect1" x="10" y="10" width="200" height="100"></rect>
</svg>
This polyfill extends the SVGElement
prototype with a checkIntersection
function that imit the native implementation. It checks if the 2 elements intersect using a simple algorithm based on rectangle intersection. If the two bounding boxes intersect, it creates an SVGRect
object representing the intersection and checks if it has a positive area, indicating that the two elements actually intersect.
checkEnclosure
is probably the easiest to implement:
const checkEnclosurePolyfill = function(element, rect) {
var root = this.ownerSVGElement || this;
// Get the bounding boxes of the two elements
var bbox1 = rect;
var bbox2 = element.getBBox();
// Check if bbox2 is completely enclosed by bbox1
return bbox1.x <= bbox2.x &&
bbox1.y <= bbox2.y &&
bbox1.x + bbox1.width >= bbox2.x + bbox2.width &&
bbox1.y + bbox1.height >= bbox2.y + bbox2.height;
};
if (!SVGElement.prototype.checkEnclosure) {
SVGElement.prototype.checkEnclosure = checkEnclosurePolyfill;
}
// The code below is for the snippet only
SVGElement.prototype.checkEnclosure2 = checkEnclosurePolyfill;
const mySVG = document.getElementById('mySVG');
const myRect1 = document.getElementById('myRect1');
const myRect2 = mySVG.createSVGRect();
myRect2.width = myRect2.height = 250;
myRect2.x = myRect2.y = 0;
console.log('Original', mySVG.checkEnclosure(myRect1, myRect2));
console.log('Polyfill', mySVG.checkEnclosure2(myRect1, myRect2));
svg {
display: block;
border: 1px solid #000;
margin: 20px 0;
visibility: visible;
}
rect, circle {
fill: rgba(255, 0, 0, 0.2);
visibility: visiblePainted;
}
<svg id="mySVG" width="500" height="400">
<rect id="myRect1" x="10" y="10" width="200" height="100"></rect>
</svg>
This polyfill extends the SVGElement
prototype with a checkEnclosure
function that imit the native implementation. It checks if the first element is completely enclosed by the second one.