Skip to content

Commit 68fd769

Browse files
committed
Feat: improve perf of naturalDisplay to cache for a given set of stylesheets
1 parent a47700f commit 68fd769

File tree

1 file changed

+93
-50
lines changed

1 file changed

+93
-50
lines changed

packages/query/src/query.js

Lines changed: 93 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
findIndex,
55
firstMatch,
66
get,
7+
hashCode,
78
inArray,
89
isArray,
910
isClient,
@@ -50,6 +51,12 @@ export class Query {
5051
*/
5152
static behaviors = new Map();
5253

54+
/*
55+
Cache for naturalDisplay() results per element
56+
Key: element, Value: { rulesHash, displayValue }
57+
*/
58+
static elementDisplayCache = new WeakMap();
59+
5360
constructor(selector, { root = document, pierceShadow = false, prevObject = null } = {}) {
5461
let elements = [];
5562

@@ -1451,9 +1458,36 @@ export class Query {
14511458

14521459
naturalDisplay() {
14531460
const displays = this.map((el) => {
1461+
console.log('hi');
1462+
// Create cache key from current stylesheet state + inline styles
1463+
const stylesheetData = Array.from(document.styleSheets).map(sheet => {
1464+
try {
1465+
return { href: sheet.href, title: sheet.title, disabled: sheet.disabled };
1466+
}
1467+
catch (e) {
1468+
// Cross-origin stylesheet - can't access properties safely
1469+
return { crossOrigin: true, index: Array.from(document.styleSheets).indexOf(sheet) };
1470+
}
1471+
});
1472+
1473+
// Include inline display style in cache key since it overrides everything
1474+
const inlineDisplay = el.style.display;
1475+
const cacheKey = { stylesheetData, inlineDisplay };
1476+
const rulesHash = hashCode(cacheKey);
1477+
1478+
// Check cache for this element
1479+
const cached = Query.elementDisplayCache.get(el);
1480+
if (cached && cached.rulesHash === rulesHash) {
1481+
return cached.displayValue;
1482+
}
1483+
14541484
// If already visible, return current display
14551485
const current = getComputedStyle(el).display;
1456-
if (current !== 'none') { return current; }
1486+
if (current !== 'none') {
1487+
// Cache visible elements too
1488+
Query.elementDisplayCache.set(el, { rulesHash, displayValue: current });
1489+
return current;
1490+
}
14571491

14581492
const matchingRules = [];
14591493

@@ -1505,60 +1539,69 @@ export class Query {
15051539
// Sort by specificity, then source order
15061540
matchingRules.sort((a, b) => b.specificity - a.specificity || b.sourceOrder - a.sourceOrder);
15071541

1542+
let displayValue;
1543+
15081544
// If we have matching rules, return the highest precedence value
15091545
if (matchingRules.length > 0) {
1510-
return matchingRules[0].display;
1511-
}
1512-
1513-
// No CSS rules found - use element's natural display value
1514-
const naturalDisplay = {
1515-
inline: [
1516-
'a',
1517-
'abbr',
1518-
'b',
1519-
'bdi',
1520-
'bdo',
1521-
'br',
1522-
'cite',
1523-
'code',
1524-
'dfn',
1525-
'em',
1526-
'i',
1527-
'kbd',
1528-
'mark',
1529-
'q',
1530-
'ruby',
1531-
'samp',
1532-
'small',
1533-
'span',
1534-
'strong',
1535-
'sub',
1536-
'sup',
1537-
'time',
1538-
'u',
1539-
'var',
1540-
'wbr',
1541-
],
1542-
'inline-block': ['button', 'img', 'input', 'meter', 'object', 'progress', 'select', 'textarea'],
1543-
'table': ['table'],
1544-
'table-row': ['tr'],
1545-
'table-cell': ['td', 'th'],
1546-
'table-header-group': ['thead'],
1547-
'table-row-group': ['tbody'],
1548-
'table-footer-group': ['tfoot'],
1549-
'table-caption': ['caption'],
1550-
'table-column': ['col'],
1551-
'table-column-group': ['colgroup'],
1552-
'list-item': ['li'],
1553-
};
1546+
displayValue = matchingRules[0].display;
1547+
}
1548+
else {
1549+
// No CSS rules found - use element's natural display value
1550+
const naturalDisplay = {
1551+
inline: [
1552+
'a',
1553+
'abbr',
1554+
'b',
1555+
'bdi',
1556+
'bdo',
1557+
'br',
1558+
'cite',
1559+
'code',
1560+
'dfn',
1561+
'em',
1562+
'i',
1563+
'kbd',
1564+
'mark',
1565+
'q',
1566+
'ruby',
1567+
'samp',
1568+
'small',
1569+
'span',
1570+
'strong',
1571+
'sub',
1572+
'sup',
1573+
'time',
1574+
'u',
1575+
'var',
1576+
'wbr',
1577+
],
1578+
'inline-block': ['button', 'img', 'input', 'meter', 'object', 'progress', 'select', 'textarea'],
1579+
'table': ['table'],
1580+
'table-row': ['tr'],
1581+
'table-cell': ['td', 'th'],
1582+
'table-header-group': ['thead'],
1583+
'table-row-group': ['tbody'],
1584+
'table-footer-group': ['tfoot'],
1585+
'table-caption': ['caption'],
1586+
'table-column': ['col'],
1587+
'table-column-group': ['colgroup'],
1588+
'list-item': ['li'],
1589+
};
15541590

1555-
const tagName = el.tagName.toLowerCase();
1556-
for (const [display, tags] of Object.entries(naturalDisplay)) {
1557-
if (tags.includes(tagName)) {
1558-
return display;
1591+
const tagName = el.tagName.toLowerCase();
1592+
for (const [display, tags] of Object.entries(naturalDisplay)) {
1593+
if (tags.includes(tagName)) {
1594+
displayValue = display;
1595+
break;
1596+
}
15591597
}
1598+
displayValue = displayValue || 'block'; // Default for most elements
15601599
}
1561-
return 'block'; // Default for most elements
1600+
1601+
// Cache the result
1602+
Query.elementDisplayCache.set(el, { rulesHash, displayValue });
1603+
1604+
return displayValue;
15621605
});
15631606
return displays.length > 1 ? displays : displays[0];
15641607
}

0 commit comments

Comments
 (0)