Skip to content

Commit 2cd310c

Browse files
committed
Fix the previously found issue
1 parent cf712b6 commit 2cd310c

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

packages/which-heading-do-i-need/src/index.ts

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,18 @@ function isRoot(element: Element) {
3333
return element === document.body || element.id === TEST_BOUNDARY;
3434
}
3535

36-
function findHeadingIn(node: ParentNode): number | undefined {
36+
/**
37+
* node is our current traversal node as we head up the DOM tree (towards the root)
38+
* previous node is the immediate child within that current traversal node that we came from.
39+
*
40+
* The previousNode is used for an optimization so that we can iterate *from*
41+
* that child within the current traversal node, rather than always starting from the
42+
* first child of the current traversal node.
43+
*/
44+
function findHeadingIn(
45+
node: ParentNode | ChildNode,
46+
previousNode?: ParentNode,
47+
): number | undefined {
3748
if (!(node instanceof Element)) return;
3849

3950
if (SECTION_HEADINGS.has(node.tagName)) {
@@ -42,6 +53,42 @@ function findHeadingIn(node: ParentNode): number | undefined {
4253
return level;
4354
}
4455

56+
/**
57+
* Previous traversal does not search within the section boundaies
58+
* This is because previous traversal is looking for a similar heading level, and crossing a section boundary changes the section level.
59+
*/
60+
if (previousNode) {
61+
let previous = previousNode.previousSibling;
62+
63+
while (previous) {
64+
if (!(previous instanceof Element)) {
65+
previous = previous.previousSibling;
66+
continue;
67+
}
68+
69+
if (BOUNDARY_ELEMENTS.has(previous.tagName)) {
70+
previous = previous.previousSibling;
71+
continue;
72+
}
73+
74+
const level = findHeadingIn(previous);
75+
76+
/**
77+
* We subtract one, because we may have found
78+
* an equal heading, due to it being a sibling
79+
*/
80+
if (level) return level;
81+
82+
previous = previous.previousSibling;
83+
}
84+
}
85+
86+
/**
87+
* Fallback traversal if we still haven't found the
88+
* heading level, we check all the children
89+
* of the current node, because headings can be
90+
* within <a> tags and such.
91+
*/
4592
for (const child of node.children) {
4693
const level = findHeadingIn(child);
4794

@@ -116,10 +163,11 @@ function levelOf(node: Text): number {
116163
);
117164
}
118165

166+
let previous: ParentNode = ourBoundary;
119167
let current: ParentNode | null = ourBoundary.parentNode;
120168

121169
while (current) {
122-
const level = findHeadingIn(current);
170+
const level = findHeadingIn(current, previous);
123171

124172
if (level) {
125173
return level + 1;
@@ -131,12 +179,21 @@ function levelOf(node: Text): number {
131179
current = current.host;
132180
}
133181

182+
previous = current;
134183
current = current.parentNode;
135184
}
136185

137186
return 1;
138187
}
139188

189+
/**
190+
* Determines what your heading level should be (h1 - h6).
191+
*
192+
* In your app, you can use any of `<section>`, `<article>`, and `<aside>` elements to denote when the [_Section Heading_][mdn-h] element should change its level.
193+
Note that this demo starts with `h3`, because this docs page already has an `h1`, and _this_ section (Usage) uses an `h2`.
194+
195+
* [mdn-h]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/Heading_Elements
196+
*/
140197
export function getSectionHeadingLevel(node: Text) {
141198
const existing = LOOKUP.get(node);
142199

test-app/tests/heading/rendering-test.gts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ module('Rendering | Heading', function (hooks) {
253253
await render(
254254
<template>
255255
<h1 id="a">one</h1>
256-
<h1 id="c">three</h1>
256+
<h2 id="c">three</h2>
257257
<section>
258258
<Heading id="b">two</Heading>
259259
</section>
@@ -411,6 +411,7 @@ module('Rendering | Heading', function (hooks) {
411411

412412
assert.dom('#a').hasTagName('h1');
413413
assert.dom('#b').hasTagName('h2');
414+
414415
assert.dom('#c').hasTagName('h2');
415416
assert.dom('#d').hasTagName('h3');
416417
assert.dom('#e').hasTagName('h3');

0 commit comments

Comments
 (0)