Skip to content
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

Introduce DOM post-connection steps #1261

Merged
merged 14 commits into from
Jun 5, 2024
85 changes: 83 additions & 2 deletions dom.bs
Original file line number Diff line number Diff line change
Expand Up @@ -2645,12 +2645,69 @@ of a <var>node</var> into a <var>parent</var> before a <var>child</var>, run the

<p><a lt="Other applicable specifications">Specifications</a> may define
<dfn export id=concept-node-insert-ext>insertion steps</dfn> for all or some <a for=/>nodes</a>. The
algorithm is passed <var ignore>insertedNode</var>, as indicated in the <a for=/>insert</a>
algorithm below.
algorithm is passed <var>insertedNode</var>, as indicated in the <a for=/>insert</a> algorithm
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
below. These steps must not modify the <a>node tree</a> that <var>insertedNode</var>
<a>participates</a> in, create <a for=/>browsing contexts</a>,
<a lt="fire an event">fire events</a>, or otherwise execute JavaScript. These steps may
[=queue a global task|queue tasks=] to do these things asynchronously, however.

<div class=example id=example-foo-what-do-i-put-here>
<p>While the <a>insertion steps</a> cannot execute JavaScript (among other things), they will
indeed have script-observable consequences. Consider the below example:

<pre class=lang-javascript><code>
const h1 = document.querySelector('h1');

const fragment = new DocumentFragment();
const script = fragment.appendChild(document.createElement('script'));
const style = fragment.appendChild(document.createElement('style'));

script.innerText= 'console.log(getComputedStyle(h1).color)'; // Logs 'rgb(255, 0, 0)'
style.innerText = 'h1 {color: rgb(255, 0, 0);}';

document.body.append(fragment);
</code></pre>

<p>The script in the above example logs <code class=lang-javascript>'rgb(255, 0, 0)'</code> because
the following happen in order:

<ol>
<li><p>The <a for=/>insert</a> algorithm runs, which will insert the <{script}> and <code>
<a element spec=HTML>style</a></code> elements in order.

<ol>
<li><p>The HTML Standard's <a>insertion steps</a> run for the <{script}> element; they do
nothing. [[!HTML]]

<li><p>The HTML Standard's <a>insertion steps</a> run for the <code>
<a element spec=HTML>style</a></code> element; they immediately apply its style rules to the
document. [[!HTML]]

<li><p>The HTML Standard's <a>post-connection steps</a> run for the <{script}> element; they run
the script, which immediately observes the style rules that were applied in the above step.
[[!HTML]]
</ol>
</li>
</ol>
</div>

<!-- See https://github.com/whatwg/dom/issues/34#issuecomment-125571750 for why we might need to
adjust this further based on the requirements of the script element. There might be other ways
to define that though as Olli suggests, so leaving that out for now. -->

<p><a lt="Other applicable specifications">Specifications</a> may also define
<dfn export id=concept-node-post-connection-ext>post-connection steps</dfn> for all or some
<a for=/>nodes</a>. The algorithm is passed <var ignore>connectedNode</var>, as indicated in the
<a for=/>insert</a> algorithm below.

domfarolino marked this conversation as resolved.
Show resolved Hide resolved
<p class=note>The purpose of the <a>post-connection steps</a> is to provide an opportunity for
<a for=/>nodes</a> to perform any connection-related operations that modify the <a>node tree</a>
that <var ignore>connectedNode</var> <a>participates</a> in, create <a for=/>browsing contexts</a>,
or otherwise execute JavaScript. These steps allow a batch of <a>nodes</a> to be
<a for=/>inserted</a> <em>atomically</em> with respect to script, with all major side effects
occurring <em>after</em> the batch insertions into the <a>node tree</a> is complete. This ensures
that all pending <a>node tree</a> insertions completely finish before more insertions can occur.

<p><a lt="other applicable specifications">Specifications</a> may define
<dfn export id=concept-node-children-changed-ext>children changed steps</dfn> for all or some
<a for=/>nodes</a>. The algorithm is passed no argument and is called from <a for=/>insert</a>,
Expand Down Expand Up @@ -2752,6 +2809,30 @@ before a <var>child</var>, with an optional <i>suppress observers flag</i>, run
<var>parent</var> with <var>nodes</var>, « », <var>previousSibling</var>, and <var>child</var>.

<li><p>Run the <a>children changed steps</a> for <var>parent</var>.

<li>
<p>Let <var>staticNodeList</var> be a <a for=/>list</a> of <a for=/>nodes</a>, initially « ».</p>

<p class="note">We collect all <a for=/>nodes</a> <em>before</em> calling the
<a>post-connection steps</a> on any one of them, instead of calling the
<a>post-connection steps</a> <em>while</em> we're traversing the <a>node tree</a>. This is because
the <a>post-connection steps</a> can modify the tree's structure, making live traversal unsafe,
possibly leading to the <a>post-connection steps</a> being called multiple times on the same
<a>node</a>.</p>
</li>

<li>
<p>For each <var>node</var> of <var>nodes</var>, in <a>tree order</a>:

<ol>
<li><p>For each <a>shadow-including inclusive descendant</a> <var>inclusiveDescendant</var> of
<var>node</var>, in <a>shadow-including tree order</a>, <a for=list>append</a>
<var>inclusiveDescendant</var> to <var>staticNodeList</var>.
</ol>
</li>
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
domfarolino marked this conversation as resolved.
Show resolved Hide resolved

<li><p><a for=list>For each</a> <var>node</var> of <var>staticNodeList</var>, if <var>node</var> is
<a>connected</a>, then run the <a>post-connection steps</a> with <var>node</var>.
</ol>


Expand Down
Loading