1-0_PABYfDH4JN8GDzg2rgvA.jpeg
Strategy

React or Vue: Which JavaScript UI Library Should You Be Usin…


In 2016 React cemented its position as king of the JavaScript web frameworks. This year saw the rapid growth of both its web and native mobile libraries, and a comfortable lead over its main rival Angular.

But 2016 was an equally impressive year for Vue. The release of its version 2 made a huge impression on the JavaScript community, attested to by the 25,000 extra Github stars it gained that year.

The scope of both React and Vue is undeniably similar: both are lightweight component-based libraries for building user interfaces that focus on the view layer only. Both can be used in a simple project, or be scaled up to a sophisticated app using cutting edge tooling.

As a result, a lot of web developers are wondering which one they should be using. Is one clearly superior over the other? Do they have specific pros and cons to be aware of? Or are they basically the same?

Two Frameworks, Two Advocates

In this article, I want to answer those questions with a thorough and fair comparison. The only problem is: I’m an unashamed Vue fanboy and totally biased. I’ve used Vue heavily in my projects and even released an online course, the Ultimate Vue.js Developers course.

To even out my biased position I’ve brought in my friend Alexis Mangin who is both a great JavaScript developer and a big React fan. He’s similarly immersed in React, using it frequently in both web and mobile projects.

Alexis asked me one day: “why are you so into Vue, and not React?” Since I didn’t know React that well, I couldn’t give a good answer. So I put the idea to him that we sit down one day with our laptops and show each other what our chosen library had to offer.

1-0_PABYfDH4JN8GDzg2rgvA.jpeg

Anthony (left) and Alexis (right) comparing React and Vue at Bull and Bear Cafe in Chiang Mai, Thailand

After a lot of discussion and learning from both sides, the following six points are our key findings.

If You Like Building Apps With Templates (or Want the Option To), Go With Vue

Putting your markup in an HTML file is the default option for a Vue app. Similar to Angular, mustache braces are used for data-binding expressions, while directives (special HTML attributes) are used for adding functionality to the template. The following demonstrates a simple Vue app. It prints a message and has a button that dynamically reverses the message:

<div id="app">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">Reverse Message</button>
</div>
new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('');
    }
  }
});

In contrast, React apps shun templates and require the developer to create their DOM in JavaScript, typically aided with JSX. Below is the same simple app implemented with React:

<div id="app"></div>
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message: 'Hello React.js!'
    };
  }
  reverseMessage() {
    this.setState({ 
      message: this.state.message.split('').reverse().join('') 
    });
  }
  render() {
    return (
      <div>
        <p>{this.state.message}</p>
        <button onClick={() => this.reverseMessage()}>
          Reverse Message
        </button>
      </div>
    )
  }
}
ReactDOM.render(App, document.getElementById('app'));

Templates are easier to understand for newer developers who’ve come from the standard web development paradigm. But even some experienced developers prefer them, as templates can better separate layout from functionality and give the option of using pre-processors like Pug.

But templates come at the cost of having to learn all the extended HTML syntax, while render functions only require knowledge of standard HTML and JavaScript. Render functions also benefit from easier debugging and testing.

On this point, though, you can’t go wrong with Vue, as it’s introduced the option of using either templates or render functions in version 2.

If You Like Simplicity and Things That “Just Work”, Go With Vue

A simple Vue project can be run directly from a browser with no need of transpilation. This allows Vue to be easily dropped into a project the way jQuery is.

While this is also technically possible with React, typical React code leans more heavily on JSX and on ES6 features like classes and non-mutating array methods. But Vue’s simplicity runs more deeply in its design. Let’s compare how the two libraries handle application data (i.e. “state”).

State in React is immutable so you can’t directly change it. You need to use the setState API method:

this.setState({ 
    message: this.state.message.split('').reverse().join('') 
});

Diff’ing the current and previous state is how React knows when and what to re-render in the DOM, hence the need for immutable state.

In contrast, data is just mutated in Vue. The same data property can be altered far less verbosely in Vue:

// Note that data properties are available as properties of 
// the Vue instance
this.message = this.message.split('').reverse().join('');

Before you conclude that Vue’s rendering system must lack the efficiency of React’s, let’s examine how state in Vue is managed under the hood: when you add a new object to the state, Vue will walk through all of its properties and convert them to getter and setters. Vue’s reactivity system now keeps track of the state and will automatically re-render the DOM when it is mutated.

Impressively, altering state in Vue is not only more succinct, but its re-rendering system is actually faster and more efficient than React’s.

Vue’s reactivity system does have caveats, though. For example, it cannot detect property addition or deletion and certain array changes. These cases can be worked around with a React-like set method from the Vue API.

If You Need Your Application to Be as Small and Fast as Possible, Go With Vue

Both React and Vue will build a virtual DOM and synchronize the real DOM when the app’s state changes. Both have their own means of optimizing this process. Vue core developers have offered a benchmark test that shows Vue’s rendering system to be faster than React’s. In this test, a list of 10,000 items is rendered 100 times. The comparison is tabled below.

1-xy2KzzZrqP0mdLUbIdC-xA.png

Benchmarks as published on vuejs.org

From a pragmatic standpoint, this kind of benchmark is only relevant in edge cases. Most apps will not need to do this kind of operation routinely so it should generally not be considered an important point of comparison.

Page size, though, is relevant to all projects, and again Vue has the upper hand. Minified, the current release of the Vue library is only 25.6KB.

To get a similar set of functionality in React you need React DOM (37.4KB) and the React with Addons library (11.4KB), which totals 48.8KB, almost double the size of Vue. To be fair, you will get a larger API with React, but you don’t get twice as much functionality.

If You Plan to Build a Large Scale App, Go With React

A comparison of a simple app implemented in both Vue and React, like the one at the beginning of this article, may initially bias a developer to favor Vue. This is because template-based apps are easier to understand at first look, and quicker to get up and running with.

But these initial benefits introduce technical debt that can slow development of apps reaching a larger scale. Templates are prone to unnoticed runtime errors, are hard to test, and are not easy to restructure or decompose.

In contrast, JavaScript-made templates can be organized into components with nicely decomposed and DRY code that is more reusable and testable.

Vue also has a component system and render functions. But React’s rendering system is more configurable and has features like shallow rendering that, combined with React’s testing utilities, allow for far more testable and maintainable code.

Meanwhile, React’s immutable application data may not be as succinct, but it shines in a larger application when transparency and testability become critical.

If You Want a Library That Is Adaptable for Both Web and Native Apps, Go With React

React Native is a library for building native mobile applications with JavaScript. It’s the same as React.js, only instead of using web components, it uses native components. If you’ve learned React.js, you’ll very easily be able to pick up React Native, and vice versa.

import React, { Component } from 'react'; 
import { AppRegistry, Text, View } from 'react-native';  
class HelloWorld extends Component {   
  render() {     
    return (       
      <View>         
        <Text>Hello, React Native!</Text>
      </View>
    );   
  }
}
AppRegistry.registerComponent('HelloWorld', () => HelloWorld);

The significance is that a developer can build an app on either the web or native mobile without requiring a different set of knowledge and tools. Learning React gives you a huge bang for you buck if you intend to develop for both web and mobile.

Alibaba’s Weex is another cross-platform UI project. Currently, it considers Vue an “inspiration” and uses a lot of the same syntax, with plans to fully integrate Vue. However, the timeline and specifics of this integration are still unclear.

Since Vue has HTML templates as a core part of its design and does not have custom rendering as a current feature, it’s hard to see that a native counterpart for Vue.js in its current form will be as tight as what React.js and React Native are.

If You Want the Biggest Ecosystem, Go With React

There’s no question that React is currently the more popular library with ~2.5M NPM downloads a month as opposed to Vue’s ~225K per month.

1-YTD_ZbHE77GWKzYJVGh_Fw.png

Popularity is not merely a shallow benefit. It means there are more articles, tutorials, and Stack Overflow answers for help. It means there are more tools and add-ons to leverage in a project and save developers from building everything themselves.

Both libraries are open source, but React was born from Facebook and benefits from that patronage. Developers and companies committing to React can be assured of continued maintenance.

In contrast, Vue was created by a single developer, Evan You, and You is currently the only full-time maintainer of Vue. Vue has some corporate sponsorship but not on the scale of Facebook or Google.

To the credit of the Vue team, its small size and independence have not materialized as a disadvantage. Vue has a regular release cycle and even more impressively, Vue has only 54 open issues on Github compared to 3456 closed issues, while React has a far larger 530 open issues compared to 3447 closed.

If You’re Already Happy With One or the Other, There’s No Need to Switch

To recap, our findings, Vue’s strengths are:

  • Flexible options for template or render functions.
  • Simplicity in syntax and project setup.
  • Faster rendering and smaller size.

React’s strengths:

  • Better at scale, sturdier, and more testable.
  • Web and native apps.
  • A bigger ecosystem with more support and tools available.

However, both React and Vue are exceptional UI libraries and have more similarities than differences. Most of their best features are shared:

  • Fast rendering with virtual DOM.
  • Lightweight.
  • Reactive components.
  • Server-side rendering.
  • Easy integration with router, bundler, and state management.
  • Great support and community.

If you think we’ve missed something we’d love to hear in the comments. Happy developing!


About the Authors

Anthony Gore

I’m a JavaScript developer and online course instructor. I run a blog, Vue.js Developers, and am the author of the Ultimate Vue.js Developers course.

Alexis Mangin

Alexis helped me write this article with his exceptional knowledge of web development. You should follow him on Medium as he writes his own great tutorials on React.



Source link

Image title
Strategy

Reactivity in Vue.js and Its Pitfalls


One thing we love about Vue is the reactivity of the system. If we change a data value it triggers an update of the page to reflect that change.

For example:

varapp = newVue({
  el: '#app',
  data: {
    message: "Hello World"
  }
});

setTimeout(function() {
  app.message = "Goodbye World";
}, 2000)

<divid="app">
  <!--Renders as "Hello World"...-->
  <!--Then after 2 seconds re-renders as "Goodbye World"-->
  {{ message }}
</div>

Data properties, like message in this example, are reactive, meaning they will trigger a re-render if changed.

Pitfalls of Automatic Reactivity Configuration

Vue configures reactivity automatically whenever you create a data property, computed property, bind a prop, etc. This automatic setup is great when coding an app because it can:

  • Save us time.
  • Make our code terse.
  • Help minimize our cognitive load.

It just makes things simpler. But this simplicity can come back to bite us! The pitfall is that, like an automatic car, automatic reactivity makes us lazy and when it doesn’t work we have no idea why!

When Good Reactivity Goes Bad

A student in the Ultimate Vue.js Developers course I teach brought an interesting problem to me the other day. He was working on Vue.js Poster Shop, the first project of the course, which requires you to make a shopping cart using Vue.

A product being displayed in the shop is initially represented like this:

varmyProduct = {
  id: 1,
  name: 'My Product',
  price: 9.99
};

But when you add a product to the shopping cart you also need to a quantity, which he dealt with in a method like this:

functionaddToCart(id) {
  varitem = this.cart.findById(id);
  if (item) {
    item.qty++;
  } else {
    item.qty = 1;
    this.cart.push(item);
  }
}

addToCart(myProduct.id);

The logic of the method is as follows:

  • Find the item in the shopping cart.
  • If it’s there, increase the quantity.
  • If it’s not there, give it a quantity of 1 and add it to the cart.

A Problem Appears

The shopping cart template simply displays a list of cart items:

<ul class="shopping-cart">
  <li v-for="item in cart">{{ item.name }} x {{ item.qty }}</li>
  <!-- Renders: myProduct x 1 -->
</ul>

The problem was that no matter the value of qty, the template always showed its value as “1.”

My initial thought was that the logic of the addToCart function must be wrong. But after some debugging, I discovered that the qty property was indeed being increased each time the method was called, so that wasn’t the issue.

How Reactivity Works Under the Hood

While causing a mild form of joy in most circumstances, Vue’s reactivity system can cause confusion and frustration when it doesn’t work like you expect.

This can largely be avoided if you understand how it works.

Getters and Setters

The default behavior for a JavaScript object, when accessed, is to retrieve or modify the property in question directly. For example:

varmyObj={
  a:"Hello World"
};

console.log(myObj.a)// "Hello World"

But when get and set pseudo properties have been defined, these functions override that default behavior. Read more about getters and setters if you don’t know what I’m talking about.

When a Vue instance is created, each data property, component prop, etc., is traversed and getters and setters are added for each. These getters and setters allow Vue to observe changes to the data and trigger updates.

reactiveSetter()

So, coming back to our product object which looked like this when we defined it:

{
  id:1,
  name:'My Item',
  price:9.99
}

After Vue instantiates, we can view this object in the console and see the getters and setters that Vue has defined on it:

Image title

These getter and setter functions have a number of jobs (check the source code), but one of the jobs of rectiveSetter is to trigger a change notification which results in a page re-render!

Caveats

This is a brilliant system, albeit a fallible one. If you add (or delete) a property after Vue has instantiated (for example in a method or lifecycle hook) Vue does not know about it.

// In the addToCart method, called after instantiation
myProduct.qty=1;

Look and see that although qty is defined on the object there are no getters/setters for it:

Image title

Updating Reactive Objects

In the shopping cart example, the way we solved the problem is to create a fresh object when adding to the cart rather than adding a property. That ensures that Vue has the opportunity to define reactive getters and setters:

functionaddToCart(id) {
  varitem = this.cart.findById(id);
  if (item) {
    item.qty++;
  } else {
    // Don't add a property e.g. item.qty = 1;
    // Instead create a fresh object
    varnewItem = {
      id: item.id,
      name: item.name,
      price: item.price,
      qty: 1
    };
    this.cart.push(item);
  }
}

addToCart(myProduct.id);

Vue.set

But if you don’t want to create a new object you can use Vue.set to set a new object property. This method ensures the property is created as a reactive property and triggers view updates:

functionaddToCart(id) {
  varitem = this.cart.findById(id);
  if (item) {
    item.qty++;
  } else {
    // Don't add a property directly e.g. item.qty = 1;
    // Use Vue.set to ensure the property is reactive
    Vue.set(item, 'qty', 1);
    this.cart.push(item);
  }
}

addToCart(myProduct.id);

Arrays

Like objects, arrays are reactive and are observed for changes. Also like objects, arrays have caveats for manipulation. Vue wraps array methods like push, splice, etc., so they will also trigger view updates.

This is not possible when directly setting an item with the index, for example: 

// This array change will not be detected:
app.myArray[index]=newVal;

Again, Vue.set comes to the rescue:

Vue.set(app.myArray,index,newVal);

 



Source link

Image title
Strategy

Extending Vue.js Components – DZone Web Dev


Do you have components in your Vue app that share similar options, or even template markup?

It’d be a good idea to create a base component with the common options and markup, and then extend the base component to create sub components. Such an architecture would help you apply the DRY principle in your code (Don’t Repeat Yourself) which can make your code more readable and reduce the possibility of bugs.

Vue provides some functionality to help with component inheritance, but you’ll also have to add some of your own ingenuity.

Example: Survey Questions

Here is a simple survey made with Vue.js:

Image title

You’ll notice that each question has a different associated input type:

  1. Text input
  2. Select input
  3. Radio input

A good architecture would be to make each question/input type into a different, reusable component. I’ve named them to correspond to the above:

  1. SurveyInputText
  2. SurveyInputSelect
  3. SurveyInputRadio

It makes sense that each question/input is a different component because each needs its own markup (e.g. <input type="text"> vs <input type="radio">) and each needs its own props, methods, etc., as well. However, these components will have a lot in common:

  • A question.
  • A validation function.
  • An error state.
  • Etc.

So I think this is a great use case for extending components!

Base Component

Each of the sub components will inherit from a single file component called SurveyInputBase. Notice the following:

  • The question prop is going to be common across each component. We could add more common options, but let’s stick with just one for this simple example.
  • We somehow need to copy the props from this component to any extending component.
  • We somehow need to insert the markup for the different inputs inside the template.

SurveyInputBase.vue

<template>
  <div class="survey-base">
    <h4>{{ question }}</h4>
    <!--the appropriate input should go here-->
  </div>
</template>
<script>
  export default {
    props: [ 'question' ],
  }
</script>

Inheriting Component Options

Forgetting the template for a moment, how do we get each sub component to inherit props? Each will need question as a prop, as well as their own unique props:

Image title

This can be achieved by importing the base component and pointing to it with the extends option:

SurveyInputText.vue

<template>
  <!--How to include the question here???-->
  <input :placeholder="placeholder">
</template>
<script>
  import SurveyInputBase from './SurveyInputBase.vue';
  export default {
    extends: SurveyInputBase,
    props: [ 'placeholder' ],
  }
</script>

Looking in Vue Devtools, we can see that using extends has indeed given us the base props in our sub component:

Image title

Merge Strategy

You may be wondering how the sub component inherited the question prop instead of overwriting it. The extends option implements a merge strategy that will ensure your options are combined correctly. For example, if the props have different names, they will obviously both be included, but if they have the same name, Vue will preference the sub component.

The merge strategy also works with other options like methods, computed properties, and lifecycle hooks, combining them with similar logic. Check the docs for the exact logic on how Vue does it, but if you need you can define your own custom strategy.

Note: there is also the option of using the mixin property in a component instead of extends. I prefer extends for this use case, though, as it has a slightly different merge strategy that gives the sub components options higher priority.

Extending the Template

It’s fairly simple to extend a component’s options – what about the template, though?

The merge strategy does not work with the template option. We either inherit the base template, or we define a new one and overwrite it. But how can we combine them?

My solution to this is to use the Pug pre-processor. It comes with include and extends options so it seems like a good fit for this design pattern.

Base Component

Firstly, let’s convert our base component’s template to Pug syntax:

<template lang="pug">
  div.survey-base
    h4 {{ question }}
    block input
</template>

Notice the following:

  • We add lang="pug" to our template tag to tell vue-loader to process this as a Pug template (also, don’t forget to add the Pug module to your project as well npm i --save-dev pug)
  • We use block input to declare an outlet for sub component content.

So here’s where it gets slightly messy. If we want our child components to extend this template we need to put it into its own file:

SurveyInputBase.pug

div.survey-base
  h4 {{ question }}
  block input

We then include this file in our base component so it can still be used as a normal standalone component:

SurveyInputBase.vue

<template lang="pug">
  include SurveyInputBase.pug
</template>
<script>
  export default {
    props: [ 'question' ]
  }
</script>

It’s a shame to have to do that since it kind of defeats the purpose of “single file” components, but for this use case, I think it’s worth it. You could probably make a custom webpack loader to avoid having to do this.

Sub Component

Let’s now convert our sub component’s template to Pug as well:

SurveyInputText.vue

<template lang="pug">
  extends SurveyInputBase.pug
  block input
    input(type="text" :placeholder="placeholder")
</template>
<script>
  import SurveyInputBase from './SurveyInputBase.vue';
  export default {
    extends: SurveyInputBase,
    props: [ 'placeholder' ],
  }
</script>

The sub components use the extends feature of Pug which includes the base component and outputs any custom content in the input block (a concept analogous to slots).

Here’s what the sub component’s template would effectively look like after extending the base and being translated back to a regular HTML Vue template:

<div class="survey-base">
  <h4>{% raw %}{{ question }}{% endraw %}</h4>
  <input type="text" :placeholder="placeholder">
</div>

Bring it All together

Using this strategy we can go ahead and create the other two sub components SurveyInputSelect and SurveyInputRadio with their own props and markup.

If we then use them in a project our main template might look like this:

<survey-input-text
  question="1. What is your name?"
  placeholder="e.g. John Smith"
></survey-input-text>

<survey-input-select
  question="2. What is your favorite UI framework?"
  :options="['React', 'Vue.js', 'Angular']"
></survey-input-select>

<survey-input-radio
  question="3. What backend do you use?"
  :options="['Node.js', 'Laravel', 'Ruby']"
  name="backend"
>
</survey-input-radio>

And here’s the rendered markup:

<div class="survey-base">
  <h4>1. What is your name?</h4>
  <input type="text" placeholder="e.g. John Smith">
</div>
<div class="survey-base">
  <h4>2. What is your favorite UI framework?</h4>
  <select>
    <option>React</option>
    <option>Vue.js</option>
    <option>Angular</option>
  </select>
</div>
<div class="survey-base">
  <h4>3. What backend do you use?</h4>
  <div><input type="radio" name="backend" value="Node.js">Node.js</div>
  <div><input type="radio" name="backend" value="Laravel">Laravel</div>
  <div><input type="radio" name="backend" value="Ruby">Ruby</div>
</div>

Note: we could also instantiate the SurveyInputBase component, since it will work standalone, but it wasn’t really needed in this example. I thought that was an important point to mention, though.



Source link

Post image
Strategy

On a webpage using an MVC architecture that uses tabs that c…


I’m trying to better understand the MVC architecture, and which functionality gets separated into which components. I’m thinking about a website that uses tabs to switch a view. The easiest one I can think of is expedia.com’s homepage, where there are several options (like whether you want to book a flight only, a hotel, etc). If you click one option, the relevant parts of the view change to have different options for the user to enter. See here:

Post image

So I’m wondering, in the MVC architecture, are those tabs (maybe buttons?) part of the View itself, or the Controller?

If part of the View, then am I right to say that the Controller sent over all of the information for all of the options, and the View is just choosing what to display when the user clicks a tab? That makes the most sense to me.

A second question regarding this: is it ever possible for part of the Controller to be on the client? If not I suppose this question is moot, since I believe that no http requests are made once a user clicks a tab, and the only time the requests happen is when you hit “search”.



Source link

Adding Dynamic And Async Functionality To JAMstack Sites — S...
Strategy

Helping Browsers Optimize With The CSS Contain Property — Sm…


About The Author

Rachel Andrew is not only Editor in Chief of Smashing Magazine, but also a web developer, writer and speaker. She is the author of a number of books, including …
More about
Rachel
Andrew

The CSS contain property gives you a way to explain your layout to the browser, so performance optimizations can be made. However, it does come with some side effects in terms of your layout.

In this article, I’m going to introduce a CSS Specification that has just become a W3C Recommendation. The CSS Containment Specification defines a single property, contain, and it can help you to explain to the browser which parts of your layout are independent and will not need recalculating if some other part of the layout changes.

While this property exists for performance optimization reasons, it can also affect the layout of your page. Therefore, in this article, I’ll explain the different types of containment you can benefit from, but also the things you need to watch out for if applying contain to elements in your site.

The Problem Of Layout Recalculation

If you are building straightforward web pages that do not dynamically add or change elements after they have loaded using JavaScript, you don’t need to worry about the problem that CSS Containment solves. The browser only needs to calculate your layout once, as the page is loaded.

Where Containment becomes useful is when you want to add elements to your page without the user needing to reload it. In my example, I created a big list of events. If you click the button, the first event is modified, a floated element is added, and the text is changed:

A listing of items with a button to change some of the content in the first item
(See the initial example on CodePen)

When the content of our box is changed, the browser has to consider that any of the elements may have changed. Browsers are in general pretty good at dealing with this, as it’s a common thing to happen. That said, as the developer, you will know if each of the components is independent, and that a change to one doesn’t affect the others, so it would be nice if you could let the browser know this via your CSS. This is what containment and the CSS contain property gives you.

How Does Containment Help?

An HTML document is a tree structure which you can see when inspecting any element with DevTools. In my example above, I identify one item that I want to change by using JavaScript, and then make some changes to the internals. (This means that I’m only changing things inside the subtree for that list item.)

DevTools with the list item of the featured item expanded to see the elements inside
Inspecting a list item in DevTools

Applying the contain property to an element tells the browser that changes are scoped to the subtree of that element, so that the browser can do any possible optimizations — safe in the knowledge that nothing else outside of that element will change. Exactly what a particular browser might do is down to the engine. The CSS property simply gives you — as the developer and expert on this layout — the chance to let it know.

In many cases, you will be safe to go right ahead and start using the contain property, however, the different values come with some potential side effects which are worth understanding before adding the property to elements in your site.

Using Containment

The contain property can set three different types of containment:

Note: There is a style value in the Level 2 Specification. It was removed from Level 1, so does not appear in the Recommendation, and is not implemented in Firefox.

Layout

Layout containment brings the biggest benefits. To turn on layout containment, use the following snippet:

.item {
  contain: layout;
}

With layout containment enabled, the browser knows that nothing outside the element can affect the internal layout, and nothing from inside the element can change anything about the layout of things outside it. This means that it can make any possible optimizations for this scenario.

A few additional things happen when layout containment is enabled. These are all things which ensure that this box and contents are independent of the rest of the tree.

The box establishes an independent formatting context. This ensures that the content of the box stays in the box — in particular floats will be contained and margins will not collapse through the box. This is the same behavior that we switch on when we use display: flow-root as in explained in my article “Understanding CSS Layout And The Block Formatting Context”. If a float could poke out of your box, causing following text to flow around the float, that would be a situation where the element was changing the layout of things outside it, making it a poor candidate for containment.

The containing box acts as the containing block for any absolutely or fixed position descendants. This means it will act as if you had used position: relative on the box you have applied contain: layout.

The box also creates a stacking context. Therefore z-index will work on this element, it’s children will be stacked based on this new context.

If we look at the example, this time with contain: layout, you can see that when the floated element is introduced it no longer pokes out the bottom of the box. This is our new Block Formatting Context in action, containing the float.

A listing of items, a floated element is contained inside the bounds of the parent box
Using contain: layout the float is contained (See the layout containment example on CodePen)

Paint

To turn on paint containment, use the following:

.item {
  contain: paint;
}

With paint containment enabled, the same side effects as seen with layout containment occur: The containing box becoming an independent formatting context, a containing block for positioned elements, and establishing a stacking context.

What paint containment does is indicate to the browser that elements inside the containing block will not be visible outside of the bounds of that box. The content will essentially be clipped to the box.

We can see this happen with a simple example. Even if we give our card a height, the floated item still pokes out the bottom of the box, due to the fact that the float is taken out of flow.

A floated box poking out the bottom of a containing box
The float is not contained by the list item

With paint containment turned on the floated item is now clipped to the size of the box. Nothing can be painted outside of the bounds of the element with contain: paint applied.

A box with a floated box inside which has been cut off where it escapes the box
The content of the box is clipped to the height of the box (See the paint example on CodePen)

Size

Size containment is the value that is most likely to cause you a problem if you aren’t fully aware of how it works. To apply size containment, use:

.item {
  contain: size;
}

If you use size containment then you are telling the browser that you know the size of the box and it is not going to change. This does mean that if you have a box which is auto-sized in the block dimension, it will be treated as if the content has no size, therefore the box will collapse down as if it had no contents.

In the example below, I have not given the li a height; they also have contain: size applied. You can see that all of the items have collapsed as if they had no content at all, making for a very peculiar looking listing!

A listing of items with a button to change some of the content in the first item
(See the size example on CodePen)

If you give the boxes a height then the height will be respected when contain: size is used. Alone, size containment will not create a new formatting context and therefore does not contain floats and margins as layout and paint containment will do. It’s less likely that you would use it alone; instead, it is most likely you would apply it along with other values of contain to be able to get the most possible containment.

Shorthand Values

In most cases, you can use one of two shorthand values to get the best out of containment. To turn on layout and paint containment, use contain: content;, and to turn on all possible containment (keeping in mind that items which do not have a size will then collapse), use contain: strict.

The Specification says:

contain: content is reasonably “safe” to apply widely; its effects are fairly minor in practice, and most content won’t run afoul of its restrictions. However, because it doesn’t apply size containment, the element can still respond to the size of its contents, which can cause layout-invalidation to percolate further up the tree than desired. Use contain: strict when possible, to gain as much containment as you can.”

Therefore, if you do not know the size of the items in advance, and understand the fact that floats and margins will be contained, use contain: content. If you do know the size of items in addition to being happy about the other side effects of containment, use contain: strict. The rest is down to the browser, you have done your bit by explaining how your layout works.

Can I Use Containment Now?

The CSS Containment specification is now a W3C Recommendation which is what we sometimes refer to as a web standard. In order for the spec to get to this stage, there needed to be two implementations of the feature which we can see in both Firefox and Chrome:

Screenshot of the browser support information on Containment on Can I Use
Browser support for containment (Source: Can I Use)

As this property is transparent to the user, it is completely safe to add to any site even if you have lots of visitors in browsers that do not support it. If the browser doesn’t support containment then the visitor gets the experience they usually get, those in supporting browsers get the enhanced performance.

I would suggest that this is a great thing to add to any components you create in a component or pattern library, if you are working in this way it is likely each component is designed to be an independent thing that does not affect other elements on the page, making contain: content a useful addition.

Therefore, if you have a page which is adding content to the DOM after load, I would recommend giving it a try — if you get any interesting results let me know in the comments!

The following resources will give you some more detail about the implementation of containment and potential performance benefits:

Smashing Editorial(il)



Source link