Throw out “questions”

To clarify, we are writing a common component for novice guidance today, something like this:

I’m sure the first thing that comes to mind is to get the position of the element (ID) in useEffect by getBoundingClientRect(), and then add a similar popover effect by positioning.

When I naively thought that this could achieve it, I encountered a “don’t know how to start” to solve the problem.

UseEffect getBoundingClientRect() is random?

Random?? As a basic programmer, random code execution results, how can I accept this!

Let’s look at the simplified code:

“Problem” code

// The code is already a very simplified version that only preserves the core content
import React, { useEffect } from 'react'
import './_index.scss'

const GuideBeta = () = > {
  useEffect(() = > {
    console.log(document.getElementById('step1'))
    console.log(document.getElementById('step1')? .getBoundingClientRect()) }, [])return (
    <div>
      <div className='beta'>
        <div id='step1'>
          <div>The first guideline</div>
        </div>
        <div id='step2'>
          <div>Second guideline</div>
        </div>
      </div>
    </div>)}export { GuideBeta }
Copy the code

The above code is actually quite simple: render two elements with id step1 and step2, and print the element with ID step1 in useEffect().

The page would render like this:

The output

This is normal output:

When we tried to refresh the page a few times to see the print:

Maybe you will wonder if there is something wrong with my code. Here is a small joke about the two different print results. The reason has nothing to do with the business code.

To understand this problem, we need to work our way up from some basic theoretical knowledge.

Blood and tears of lessons, I checked out my code all morning…

Browser loading mechanism

In fact, I believe that we have already talked about the browser loading mechanism, here I combine the above two different printing principles to talk about the corresponding mechanism:

jsThe execution browser will bejsThe engine “hogs”, causing the rendering process to be unable to perform blockingDomTreeRender of, thenCss?cssWhether the load will blockDom TreeHow about rendering?

Let’s talk about whether CSS blocks Dom Tree builds with this question in mind.

cssWhether the load will blockDom TreeRender and parse

validationcssThe loading andDom TreeThe relationship between

Let’s try this code first:

<! DOCTYPEhtml>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <style>
    #h1 {
      color: blue;
    }
  </style>
  <script>
    setTimeout(() = > {
      const h1 = document.getElementById('h1')
      console.log(h1)
    }, 0)
  </script>
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>

<body>
  <h1 id="h1">Big headline</h1>
</body>

</html>
Copy the code

The code is actually very simple, is in the JS script timer to get h1 tags. Then the Bootstrap style library was introduced.

Note: We need to limit the “network” in the browser to SLOW 3G for testing.

As shown above, we can see when the page is loading. SetTimeout in the js script has successfully printed the element corresponding to the H1 tag in the console.

So we can get the Dom before the CSS is loaded,

So CSS loading does not block Dom Tree building.

Note, however, that the page will not render until the CSS file is loaded with large blue headings, meaning that the page will not render until the CSS file is loaded.

CSS loading blocks the Render Tree, which we’ll discuss later.

cssforDom Treeconclusion

Let’s talk about our conclusions about CSS loading:

  1. cssLoading does not blockDom TreeBecausecssWe can get the corresponding one before the load is finishedh1The label.
  2. cssLoading will blockDom TreeRender only whencssThe page will not be rendered blue until it is loadedBig headline.

cssLoad forjsThe influence of

So does CSS loading have an effect on JS? Without further ado, let’s look at the code:

cssLoad forjsvalidation

<! DOCTYPEhtml>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <script>
    const now = window.now = Date.now()
    console.log('Before CSS loads', now)
  </script>
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
  <script>
    const scriptExec = Date.now() - window.now + 'ms'
    console.log('CSS loading complete')
    console.log('interval: + scriptExec)
  </script>
</head>

<body>
  <h1 id="h1">Big headline</h1>
</body>

</html>
Copy the code

Let’s first look at the result of this code execution, also in SLOW 3G:

We can see that there is a 2550ms difference between the two scripts, which is exactly after the CSS code is loaded.

cssLoad forjsconclusion

Similarly, we know that there is no doubt that the JS code is loaded before the CSS code, but the loading of the CSS code after the CSS code will block the execution of the subsequent JS code.

cssLoad the conclusion

Here’s a quick summary of what we’ve learned so far about CSS loading:

  1. cssCode loading does not blockDom TreeBuild.
  2. cssLoading code will blockDom TreeRender in the browser.
  3. cssCode loading is blocked laterjsCode execution.

causecssPrinciple of loading

We have already summarized the performance and summary of CSS loading for Dom Tree, JS, and Render Tree. Now let’s look at the reasons for this:

The layout->paint-> Composite key render frames involve some remodeling and backflow that I’ll explain in detail later on.

Let’s focus on thatHTMLandCssIn fact, they are parallel loading, which confirms what we mentioned abovecssLoading does not affectDom TreeBuild.

As you can see, csSOM and DOM Tree will be merged into a Render tree, and the browser will Render according to the elements and layout of the Render tree, which will not Render until the CSS file is loaded.

Both the browser rendering engine and the JS interpretation engine are mutually exclusive, meaning that CSS loading and DOM loading are mutually exclusive with JS execution loading. (Excluding defer and Async on the SCirpt tag, of course).

This is about the browser loading principles section, I believe that a practical reading of the principles will be impressive. Combined with the last two Demo examples, I believe that you can well support our conclusion with the principle of thinking.

Let’s go back to the question at the beginning of this article to find out:

Back to the question

Why is it possible to print the value of getBoundingClientRect() when the Dom element we get in useEffect is fine?

I’m sure you can guess the result, yes! It has nothing to do with our business code, it all depends on the loading of the CSS file!! (Really pit miserably me 😭)

Analysis of occasional normal conditions

Let’s take a look at the Net Work control panel when printing correctly:

  • console.jsIt is ourreactCode, containing the corresponding business logic.
  • console.cssIt’s our businesscssCode that contains the corresponding element location definition.

As we can see, our CSS code is loaded long before the JS code is loaded, which means that the page is actually rendered properly before the JS code executes (csSOM and domTree make the correct render Tree). So we’re done with useEffect and we get the correct location getBoundingClientRect().

Analysis of the occasional abnormal situation

Let’s look at the result of the occasional abnormal getBoundingClientRect print:

To explain this, let’s first look at the order of JAVASCRIPT files and CSS files in HTML:

This is the order in which the two scripts are loaded from the HEAD tag in the HTML, and the JS file references the defer attribute.

What defer means is that the JS loading will be performed asynchronously and will not block subsequent loading. According to the loading sequence, the js scripts corresponding to the completion of loading will be executed successively after the document is parsed and before the DomContentLoaded event. You can see details about defer here

The so-called DomContentLoaded event is triggered when the original HTML document has been fully loaded and parsed, without waiting for the stylesheet, image, and subframe to be fully loaded. The DomContentLoaded event is triggered when the Dom Tree is completed.

This means that our script will load asynchronously until the Dom Tree is parsed and executed before the DOMContentLoaded event is called.

Now let’s have a look at the corresponding network request result:

Our JS loading is completed 13ms faster than CSS loading. When JS loading is completed, CSS is still requesting download. At this time, JS takes precedence over CSS because the DOM Tree has been built, which is in line with our JS execution timing. When we execute js and there is no style on the page, the value we get from getBoundingClientRect is not correct (we get the position value when there is no style).

Since the defer script has been completed, the thread is empty during the CSS loading, so the JS engine will execute the script that has been loaded. Cause JS to finish execution ahead of time with CSS.

The solution

The simplest and most straightforward way to solve this problem is to use the window.onload event.

The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets and images. This is in contrast to DOMContentLoaded, which is fired as soon as the page DOM has been loaded, without waiting for resources to finish loading.

Of course, you can do it in your own way. There are many ways to do it once you know the nature of the problem.

conclusion

Let’s summarize briefly:

  1. cssThe load is blocked laterjsCarry out, follow upjsWill wait forcssIt will not be executed until the load is complete.
  2. cssLoad does not blockDom TreeBuild.
  3. cssThe loading of the page will block the page rendering because the page is renderedRender TreeIs the need tocss omanddom treeMerge to render the page.

Tips:

On the second point, loading CSS does not block the building of the Dom Tree, but if there is a JS script behind the CSS file, the JS will block the building of the Dom Tree. Because loading CSS blocks JS execution, it indirectly blocks the building of the Dom Tree.

At the same time, different browsers may have different interpretation mechanisms, the majority of cases are for Chrome interpretation.

This is the end of the “murder case” caused by business in the article. We have explained the corresponding mechanism and why to do.

Of course the browser implementation mechanism I believe that the article is still relatively one-sided, if interested we can communicate with each other in the comments section.