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:
js
The execution browser will bejs
The engine “hogs”, causing the rendering process to be unable to perform blockingDomTree
Render of, thenCss
?css
Whether the load will blockDom Tree
How about rendering?
Let’s talk about whether CSS blocks Dom Tree builds with this question in mind.
css
Whether the load will blockDom Tree
Render and parse
validationcss
The loading andDom Tree
The 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.
css
forDom Tree
conclusion
Let’s talk about our conclusions about CSS loading:
css
Loading does not blockDom Tree
Becausecss
We can get the corresponding one before the load is finishedh1
The label.css
Loading will blockDom Tree
Render only whencss
The page will not be rendered blue until it is loadedBig headline
.
css
Load forjs
The influence of
So does CSS loading have an effect on JS? Without further ado, let’s look at the code:
css
Load forjs
validation
<! 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.
css
Load forjs
conclusion
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.
css
Load the conclusion
Here’s a quick summary of what we’ve learned so far about CSS loading:
css
Code loading does not blockDom Tree
Build.css
Loading code will blockDom Tree
Render in the browser.css
Code loading is blocked laterjs
Code execution.
causecss
Principle 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 thatHTML
andCss
In fact, they are parallel loading, which confirms what we mentioned abovecss
Loading does not affectDom Tree
Build.
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.js
It is ourreact
Code, containing the corresponding business logic.console.css
It’s our businesscss
Code 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 toDOMContentLoaded
, 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:
css
The load is blocked laterjs
Carry out, follow upjs
Will wait forcss
It will not be executed until the load is complete.css
Load does not blockDom Tree
Build.css
The loading of the page will block the page rendering because the page is renderedRender Tree
Is the need tocss om
anddom tree
Merge 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.