preface

A few days ago a group of friends in the group asked an interview question, is about an adaptive square layout of the puzzle, first paste code. I haven’t written CSS in a long time, and I’m a little fuzzy about the details, so I decided to go back to CSS to sort out some of the points in this topic. (This article mostly used to explain the way for W3 CSS standard + MDN, if you are familiar with the standard, please skip this article)

What are the benefits of passing standard analysis? The most authoritative solution, can take detours, will not make mistakes.

Code:

<style>
.square {
    width: 30%;
    overflow: hidden;
    background: yellow;
}
.square::after {
    content: "";
    display: block;
    margin-top: 100%;
}
</style>
<div class="square"></div>

Copy the code

Effect (codepen. IO/hua1995116 /…). :

The above implementation seems simple, but if we dig deeper, the following three problems emerge.

  • :: After Does the fake element have any special magic?

  • Margin-top :100% margin-top:100%

  • Overflow: What does hidden do here?

Therefore, we will follow the above questions to explain one by one.

All demos in this article are hosted at github.com/hua1995116/…

::afterDoes the fake element have any special magic?

::after

Speaking of ::after then we need to talk about pseudo-elements. Let’s first look at the definition of pseudo-elements.

Pseudo elements represent abstract elements of a document, beyond those explicitly created by the document language.

– www.w3.org/TR/css-pseu…

Let’s look at :: After features:

When their computed content value is not none, these pseudo-elements generate boxes as if they were immediate children of their originating element, and can be styled exactly like any normal document-sourced element in the document tree.

According to the description, we can treat pseudo-elements as normal elements when their content is not None.

So our example is translated into something more straightforward.

<style>
.square1 {
  width: 30%;
  background: red;
  overflow: hidden;
}
.square1-after {
  margin-top:100%;
}
</style>
<div class="square1">
  <div class="square1-after"></div>
</div>
Copy the code

Speaking of pseudo-elements, let’s also review Pseudo classes, which have a very similar syntax, but water and mercury.

pseudo-classes

Is a key added to the selector that specifies the special state of the element to be selected.

: A single colon-beginning class is a pseudo-class, representing the form :hover.

/* All user Pointers hover over buttons */
button:hover {
  color: blue;
}
Copy the code

Pseudo elements

Represents abstract elements of a document that go beyond those explicitly created by the document language. (Because they are not limited to fitting into the document tree, you can use them to select and style parts of a document that don’t necessarily map to the structure of the document tree.)

:: False elements start with double colons and are in ::after form.

Pseudo-elements are applied to elements

/* The first line of each 

element. * /

p::first-line { color: blue; text-transform: uppercase; } Copy the code

Pseudo-classes and pseudo-elements

From that, we’ve probably got a general idea but we’re still a little confused about pseudo-classes and pseudo-elements that seem to have similar uses.

Key difference: Whether an element beyond the document tree is created.

At first glance ::first-line looks the same as :first-child. Let’s take an example.

// pseudo-elements.html
<style>
        p::first-line {
            color: blue;
        }
</style>
<div>
    <p>
        This is a somewhat long HTML
        paragraph that will be broken into several
        lines. 
    </p>
</div>
Copy the code
// pseudo-classes.html
<style>
        p:first-child {
            color: blue;
        }
</style>
<div>
    <p>
        This is a somewhat long HTML
        paragraph that will be broken into several
        lines.
    </p>
</div>
Copy the code

As I said, at first glance, the effects are the same. But are they really the same? Of course not! We give them width.

p {
	width: 200px;
}
Copy the code

: : first – line form

In real rendering we can understand as

<p><p::first-line>This is a somewhat long</p::first-line> HTML paragraph that will be broken into several lines.</p>
Copy the code

But we don’t see that in the real DOM Tree. This is also stated in the specification that because they do not apply exclusively to the document tree, using them to select and style documents does not necessarily map to the document tree.

Since they are not restricted to fitting into the document tree, they can be used to select and style portions of the document that do not necessarily map to the document’s tree structure.

First – the shape of the child

summary

This clears up our first problem, :: After has no magic and can be treated as a normal element in this case, and we know the difference between pseudo-elements and pseudo-classes.

margin-top:100%Why adaptive width?

We have now converted this example to a simpler form without too much knowledge.

<style>
.square1 {
  width: 30%;
  background: red;
  overflow: hidden;
}
.square1-after {
  margin-top:100%;
}
</style>
<div class="square1">
  <div class="square1-after"></div>
</div>
Copy the code

Then we look at the margin-top: 100%, which looks like it is evaluated relative to the width of the parent element. So let’s see how margin-top is calculated.

www.w3.org/TR/CSS22/bo…

It can be seen that margin-top has three main forms. The first is a fixed value, the second is a percentage, and the third is auto, so let’s focus on the percentage calculation.

The length of the margin-top margin-bottom margin-left margin-right percentage is determined by the width of the current element’s containing block.

Containing blocks

What is Containing blocks?

The position and size of an element’s box(es) are sometimes calculated relative to a certain rectangle, called the containing block of the element.

– www.w3.org/TR/CSS22/vi…

The position and size of the element box are sometimes calculated relative to a rectangle, called the containing block of the element.

That’s a bit of a mouthful, but basically all we need to know is that it’s a rectangular block. Now the important thing is, how is the inclusion block determined? (developer.mozilla.org/zh-CN/docs/…

The process of determining an element’s containing block depends entirely on the element’s position property:

  1. If the position property isstaticrelativestickyThe containing block may have been created by its nearest ancestorA block element(such as inline-block, block, or list-item elements), Formatting contexts may also be established (such as a Table Container, Flex Container, Grid Container, or the Block Container itself).
  2. If the position property isabsoluteThe containing block is not the value of its nearest positionstatic(That is, the value is zerofixed.absolute.relativesticky) is composed of the inner margin area of the ancestor element.
  3. If the position property is fixed, the continuous media block is a viewport, and the Paged media block is a Page area.
  4. If the position attribute is absolute or fixed, the include block may also consist of the edge of the inner margin region of the nearest parent element that satisfies the following conditions:
    1. A transform or perspective value other than none
    2. A will-change value of transform or perspective
    3. A filter value other than none or a will-change value of filter(only works on Firefox).
    4. A contain value of paint(such as:contain: paint;)

Note that all of the following examples have viewport widths of 594px

Case1

In the first case, which is the case in our example, the position of the current element is not filled in and defaults to static. So if the first case is satisfied, take its nearest ancestor element, which contains the block as container.

<style>
.container {
  width: 30%;
}
.inner {
  margin-top:100%;
}
</style>
<div class="container">
  <div class="inner"></div>
</div>
Copy the code

So inner margin-top = parent container = window width (594px) * 30% = 178.188px.

Case2

The current element is position:absolute, so the last position obtained is non-static

<style>
.outer {
    width: 500px;
  	position: relative;
}
.container {
    width: 30%;
}
.inner {
    position: absolute;
    margin-top: 100%;
}
</style>
<div class="outer">
  <div class="container">
    <div class="inner"></div>
  </div>
</div>
Copy the code

Margin-top = outer width (500px) * 100% = 500px.

Case3

The current element is position:fixed, and the contained block is viewport.

<style>
.outer {
    width: 500px;
  	position: relative;
}
.container {
    width: 30%;
}
.inner {
    position: absolute;
    margin-top: 100%;
}
</style>
<div class="outer">
  <div class="container">
    <div class="inner"></div>
  </div>
</div>
Copy the code

So margin-top = viewport width (594px) * 100% = 594px. This is independent of the parent element and the setting of the outer position.

Case4

Based on Case 2 and Case 3, there are special cases that affect the finding of contained blocks. Mainly in the following four cases

  1. A transform or perspective value other than none
  2. A will-change value of transform or perspective
  3. A filter value other than none or a will-change value of filter(only works on Firefox).
  4. A contain value of paint(such as:contain: paint;)

Let me show you a transform example.

<style>
.outer {
    width: 500px;
  	position: relative;
}
.container {
    width: 30%;
  	transform: translate(0, 0);
}
.inner {
    position: fixed;
    margin-top: 100%;
}
</style>
<div class="outer">
  <div class="container">
    <div class="inner"></div>
  </div>
</div>
Copy the code

At this point, our calculation changes again, and the containing block becomes the Container again.

Margin-top = parent container = window width (594px) * 30% = 178.188px.

summary

So for our initial problem, which was our Case1, we took the nearest parent element. So margin-top is the width of the parent element square1, thus implementing an adaptive square.

The different shapes of positions and their effects on layout states are generally learned in the introduction to CSS, but we may not know each case in detail, and we may not know its noun, containing block, this time we comb through it, and that’s the end of this section, continue!

overflow:hiddenWhat does it do here?

Suppose we take overflow:hidden away.

<style>
.square1 {
  width: 30%;
  background: red;
}
.square1-after {
  margin-top:100%;
}
</style>
<div class="square1">
  <div class="square1-after"></div>
</div>
Copy the code

We can see that the screen that appears after the above execution is a blank. And that brings us to our final concept, Collapsing.

Collapsing margins

In CSS, the adjacent margins of two or more boxes (which may or may not be brothers) can be merged to form a single margin, called margin collapse.

There will be no margin collapse

  • Root node element

  • Horizontal Margins will not crash

  • If the top and bottom margins of an element with a gap are adjacent, they will collapse with the margins of subsequent adjacent elements, but not with the bottom margins of the parent elementclearanceare adjoining, its margins collapse with the adjoining margins of following siblings but that resulting margin does not collapse with The bottom margin of the parent block.)

  • Parent element, parent element has non-zero min-height and auto height, both parent element contain margin-bottom, margin-bottom does not occur margin collapse.

  • In different BFC(block-level format context)

For the above, you may be confused about case 3 and case 4, so here are some examples.

case3
<style>
.case {
            width: 200px;
            background-color: yellow;
        }

        .container {
            background-color: lightblue;
            margin-bottom: 70px;
            padding-top: 0.01 px.;
        }

        .preface {
            float: left;
            height: 58px;
            width: 100px;
            border: 1px solid red;
        }

        .one .intro {
            clear: left;
            margin-top: 60px;
        }

        .two .intro {
            clear: left;
            margin-top: 59px;
            margin-bottom: 20px;
        }
</style>
<div class="case one">
        <div class="container">
            <div class="preface">
                lorem ipsum
            </div>
            <div class="intro"></div>
        </div>
        after
    </div>
    <hr>
    <div class="case two">
        <div class="container">
            <div class="preface">
                lorem ipsum
            </div>
            <div class="intro"></div>
        </div>
        after
    </div>
Copy the code

Effect in Firefox and IE (Invalid in Google, the reason may be related to the Implementation of Google Browser, not deeply followed.)

You can see that in the absence of clearance the bottom of the parent element collapses with the child element, but in the absence of clearance the bottom of the parent element does not collapse.

case4
<style>
  .case2 {
    min-height: 200px;
    height: auto;
    background: red;
    margin-bottom: 20px;
  }

  .case2-inner {
    margin-bottom: 50px;
  }
</style>
<div class="case2">
  <div class="case2-inner"></div>
</div>
<div>To see the spacing</div>
Copy the code

Effect:

You can see that in this case, there is no margin collapse on the parent and child elements.

Margin collapse occurs

Two conditions must be met for margin collapse to occur

1. Block box model, in the same BFC.

2. No inline elements, no clearance, no padding, and no border between the two elements.

Then margin collapse occurs in the following situations.

  • The top margin of the box and the top margin of the first flow into the child element
  • The bottom margin of the box and the top margin of the element that flows into it after its sibling
  • If the parent is “auto,” the last one flows into the bottom margin of the child and the bottom margin of its parent
  • An element has no new BFC created, and both min-height and height are 0, including margin-top and margin-bottom.
  • If the ‘min-height’ attribute is zero, and the box has no top or bottom border and no top or bottom padding, and the box’s ‘height’ is 0 or ‘auto’, and the box contains no margins, the box’s own margins collapse the line box and all of its inflows into the child margins (if any) collapse.

Supplement: If the ‘min-height’ attribute is zero, and the box has no top or bottom border, and no top or bottom padding, and the element’s ‘height’ is 0 or ‘auto’, and there are no inline elements, then all margins of the element itself collapse, All margins, if any, that flow into child elements collapse.

Here are a few things to explain: 1. What is the inflow child and 2. What is the clearance

1. Flow in elements

Inflow elements need to be introduced with the inverse, there are inflow elements, there are outflow elements, the following case is outflow elements.

  • Floated the items. Floating element
  • items with position: absolute (including position: fixedWhich acts in the same way. By setting the position property to absolute or fixed
  • the root element (html) the root element

Elements other than those mentioned above are called inflow elements.

<style>
body {
border: 1px solid # 000;
}

.case2 {
width: 200px;
height: 50px;
background: red;
}

.case2-inner {
margin-top: 50px;
height: 0;
}

.float {
float: left;
}
</style>
<div class="case2">
<div class="float"></div>
<div class="case2-inner">See what</div>
</div>
Copy the code

2.clearance

When an element has a clear non-None value and the box actually moves down, it is called clearance.

case1
<style>
.case1 {
            height: 50px;
            background: red;
            margin-top: 100px;
        }

        .case1-inner {
            margin-top: 50px;
        }
</style>
<div class="case1">
        <div class="case1-inner">I started right at the top</div>
    </div>
Copy the code

case2
<style>
.case2 {
            height: 150px;
            background: red;
        }

        .case2-inner1 {
            margin-bottom: 50px;
        }

        .case2-inner2 {
            margin-top: 20px;
        }
</style>
<div class="case2">
        <div class="case2-inner1">The distance between me and the bottom is 50px</div>
        <div class="case2-inner2">The distance between me and the top is 50px</div>
    </div>
Copy the code

case3
<style>
.case3 {
            height: auto;
            background: red;
            margin-bottom: 10px;
        }

        .case3-inner {
            margin-bottom: 50px;
        }
</style>
<div class="case3">
        <div class="case3-inner">The bottom and parent elements are merged</div>
    </div>
    <div>50px from the top</div>
Copy the code

case4
<style>
.case4 {
            height: 200px;
            background: red;
        }

        .case4-inner {
            margin-top: 20px;
            margin-bottom: 30px;
        }
</style>
<div class="case4">
        <p>It merges itself, 30px from the bottom</p>
        <div class="case4-inner"><span style="clear: both;"></span></div>
        <p>It merges itself, 30px from the top</p>
    </div>
Copy the code

How to solve margin collapse

universal

1. Change the box model (non-Block model)

2. Create a BFC

Restricted type

Check to see if there was no height collapse

How to calculate margin collapse

1. When two or more multimargins collapse, when all margins are positive, the resulting margin width is the maximum margin collapse width.

2. When all margins are negative, take the minimum value.

3. In the case of negative margin, subtract the absolute value of negative margin from the maximum value of positive margin. (-13px 8px 100px stacked together, 100px -13px = 87px)

If you turn it into an algorithm, this is the code

// AllList all collapse margins
function computed(AllList) {
  const PositiveList = AllList.filter((item) = > item >= 0);
  const NegativeList = AllList.filter((item) = > item <= 0);
  const AllPositive = AllList.every((item) = > item >= 0);
  const AllNegative = AllList.every((item) = > item <= 0);
  if (AllNegative) {
    return Math.min(... AllList); }else if (AllPositive) {
    return Math.max(... AllList); }else {
    const maxPositive = Math.max(... PositiveList);const minNegative = Math.min(... NegativeList);returnmaxPositive + minNegative; }}Copy the code

summary

From the above understanding of margin collapse, we can quickly conclude that in the example of our adaptive square, the margin-top of the child element and the margin-top of the parent element have collapsed, so we can create a NEW BFC to eliminate this problem. And Overflow: The hidden is that a new BFC will be formed. Landing the see developer.mozilla.org/zh-CN/docs/…

conclusion

Through the above analysis, we finally put this small interview question, a comprehensive analysis. Each problem corresponds to a block of knowledge.

  • :: After Does the fake element have any special magic? -> Pseudo elements

  • Margin-top :100% margin-top:100% -> Containing blocks

  • Overflow: What does hidden do here? -> Collapsing (Collapsing)

Surprisingly little interview questions, incredibly can get sucked out so much of the knowledge, so we in the face of some interview questions, for example to implement an adaptive square layout, don’t just look at, there are several ways to achieve the solution will always be with time becomes more and more, then we can do is in constant should change (of course standard is relative, To understand the knowledge behind these methods.

I believe that if you understand the above, the interviewer to your deep soul, you can also answer. Pay attention to some proper nouns in this article. I have highlighted them in English several times. This may help you in the future.

Hold on, we can win! Hee hee hee, finally, if you are confused about the topic for a while, welcome to add group questions, this article is also based on the questions of group friends, launched a series of explanation.

Refer to the link

Stackoverflow.com/questions/2…

www.w3.org/TR/css-disp…

Stackoverflow.com/questions/2…

Developer.mozilla.org/zh-CN/docs/…

Focus on

Welcome to follow the public account “Notes of Autumn Wind”, which mainly records the interesting tools in daily life and shares development practices to maintain depth and concentration.

Also can scan code to add my wechat friends, into the communication group.