Tag: Shadow

Styling in the Shadow DOM With CSS Shadow Parts 

Safari 13.1 just shipped support for CSS Shadow Parts. That means the ::part() selector is now supported in Chrome, Edge, Opera, Safari, and Firefox. We’ll see why it’s useful, but first a recap on shadow DOM encapsulation…

The benefits of shadow DOM encapsulation

I work at giffgaff where we have a wide variety of CSS code that has been written by many different people in many different ways over the years. Let’s consider how this might be problematic. 

Naming collisions

Naming collisions between classes can easily crop up in CSS. One developer might create a class name like .price. Another developer (or even the same one) might use the same class name, without knowing it.

CSS won’t alert you to any error here. Now, any HTML elements with this class will receive the styling intended for two completely different things.

Shadow DOM fixes this problem. CSS-in-JS libraries, like Emotion and styled-components, also solve this issue in a different way by generating random class names, like .bwzfXH. That certainly does help avoid conflicts! However, CSS-in-JS doesn’t prevent anybody from breaking your component in other ways. For example…

Base styles and CSS resets

Styles can be applied using HTML element selectors like <button> and <div>. These styles could break a component. Shadow DOM is the only thing that offers (almost) full encapsulation — you can rest assured that your component will look the same, even in a messy  !important  strewn codebase because each component is encapsulated.

/* This will have no effect on buttons inside shadow DOM */ button { background-color: lime !important; }

I wouldn’t say it’s good practice to style elements this way, but it happens. Even it does, those styles will have no effect on the shadow DOM.

It’s worth noting that inheritable styles like color, font and line-height are still inherited in a shadow DOM. To prevent that, use all: initial  or, preferably, all: revert once it has better browser support.

Let’s look at a common example of CSS applied directly to HTML elements. Consider this code from Eric Meyer’s reset

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed,  figure, figcaption, footer, header, hgroup,  menu, nav, output, ruby, section, summary, time, mark, audio, video {   margin: 0;   padding: 0;   border: 0;   font-size: 100%;   font: inherit;   vertical-align: baseline; }

What if the component we’re working with makes use of the user agent’s default values for margin and padding? This reset might cause it to appear broken since those defaults are effectively wiped out.

Shadow DOM is a way to avoid these problems. Shadow DOM allows us to feel fully confident that a component will render as expected, regardless of what codebase it ends up in. Equally, none of the code meant only for a component can inadvertently affect anything else — all without resorting to onerous class naming conventions. Shadow DOM offers a level of encapsulation that can’t be achieved any other way.

Encapsulation is great, but we also want our components to be themeable and customizable. That’s been made far easier with the ::part selector. 

Styling shadow DOM with ::part()

Until now, the only way for CSS to modify the styling of a custom element from outside of the shadow DOM was to use CSS custom properties. In a strict design system where you only want to allow limited changes, that might be ideal. If you want your component to be more versatile, it creates a problem. Every CSS property you want to offer up for styling needs to be defined using a custom property. Just the sound of that seems tedious.

The situation is compounded further if we want to style a component differently based on pseudo-classes, like :hover. Basically, we end up with loads of custom properties. Let’s look at an example from Ionic, an open source set of web components. Just look at all the custom properties defined on the Ionic button component.

Go ahead, I’ll wait.

I counted 23 custom properties. Needless to say, that’s less than ideal.

Here’s an example using ::part() to style the element instead. 

In this Pen, I’m simply changing the color, border and background-color properties, but I could use whatever I want without being constrained by what custom properties have been defined. Notice that I can also style different states of the part using pseudo-classes, like :hover and :focus.

The entire component in this button example is being exposed for styling, but if your web component consists of multiple HTML elements, you can expose only selected parts of the component to this sort of styling — hence the name ::part. This stops users of the component from styling any arbitrary element inside the shadow tree. It is up the the component author to expose the parts of the component they explicitly want to. Other parts of the component can be kept visually uniform or make use of custom properties for a more minimal customizability. 

So, how do we set this up for our own components? Let’s look at using ::part to make certain elements of a web component eligible for styling. All we do is add a part attribute on the element we want to be exposed.

<div part="box">...</div>   <button>Click me</button>

In this example the div is customizable with the full gamut of CSS — any CSS property can be changed. The button, however, is locked down — it cannot be visually changed by anybody except the component author.

And the same way an HTML element can have multiple classes, an element can have multiple part names: 

<div part="box thing">...</div>

So that’s what we get with ::part: by exposing “parts” of an element we can provide some flexibility in how a web component is used while exercising protection in other areas. Whether it’s your design system, a component library, or what have you, the fact that CSS Shadow Parts are becoming mainstream gives us yet another exciting tool to work with.

The post Styling in the Shadow DOM With CSS Shadow Parts  appeared first on CSS-Tricks.

CSS-Tricks

, ,

Make a smooth shadow, friend.

One box-shadow is cool and all, but check out Philipp Brumm’s tool for building out comma-separated multiple box-shadows, which result in a much smoother and more natural look.

This reminds me very much of the idea for easing linear-gradient. In a gradient, this smoothing effect is handled by multiple stops with the position and color eased (sloped) rather than linear (straight). The result is noticeably smoother and a more pleasing look.

See the Pen
smooth box-shadow
by Chris Coyier (@chriscoyier)
on CodePen.

Direct Link to ArticlePermalink

The post Make a smooth shadow, friend. appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Encapsulating Style and Structure with Shadow DOM

This is part four of a five-part series discussing the Web Components specifications. In part one, we took a 10,000-foot view of the specifications and what they do. In part two, we set out to build a custom modal dialog and created the HTML template for what would evolve into our very own custom HTML element in part three.

Article Series:

  1. An Introduction to Web Components
  2. Crafting Reusable HTML Templates
  3. Creating a Custom Element from Scratch
  4. Encapsulating Style and Structure with Shadow DOM (This post)
  5. Advanced Tooling for Web Components (Coming soon!)

If you haven’t read those articles, you would be advised to do so now before proceeding in this article as this will continue to build upon the work we’ve done there.

When we last looked at our dialog component, it had a specific shape, structure and behaviors, however it relied heavily on the outside DOM and required that the consumers of our element would need to understand it’s general shape and structure, not to mention authoring all of their own styles (which would eventually modify the document’s global styles). And because our dialog relied on the contents of a template element with an id of “one-dialog”, each document could only have one instance of our modal.

The current limitations of our dialog component aren’t necessarily bad. Consumers who have an intimate knowledge of the dialog’s inner workings can easily consume and use the dialog by creating their own <template> element and defining the content and styles they wish to use (even relying on global styles defined elsewhere). However, we might want to provide more specific design and structural constraints on our element to accommodate best practices, so in this article, we will be incorporating the shadow DOM to our element.

What is the shadow DOM?

In our introduction article, we said that the shadow DOM was “capable of isolating CSS and JavaScript, almost like an <iframe>.” Like an <iframe>, selectors and styles inside of a shadow DOM node don’t leak outside of the shadow root and styles from outside the shadow root don’t leak in. There are a few exceptions that inherit from the parent document, like font family and document font sizes (e.g. rem) that can be overridden internally.

Unlike an <iframe>, however, all shadow roots still exist in the same document so that all code can be written inside a given context but not worry about conflicts with other styles or selectors.

Adding the shadow DOM to our dialog

To add a shadow root (the base node/document fragment of the shadow tree), we need to call our element’s attachShadow method:

class OneDialog extends HTMLElement {   constructor() {     super();     this.attachShadow({ mode: 'open' });     this.close = this.close.bind(this);   } }

By calling attachShadow with mode: 'open', we are telling our element to save a reference to the shadow root on the element.shadowRoot property. attachShadow always returns a reference to the shadow root, but here we don’t need to do anything with that.

If we had called the method with mode: 'closed', no reference would have been stored on the element and we would have to create our own means of storage and retrieval using a WeakMap or Object, setting the node itself as the key and the shadow root as the value.

const shadowRoots = new WeakMap();  class ClosedRoot extends HTMLElement {   constructor() {     super();     const shadowRoot = this.attachShadow({ mode: 'closed' });     shadowRoots.set(this, shadowRoot);   }    connectedCallback() {     const shadowRoot = shadowRoots.get(this);     shadowRoot.innerHTML = `<h1>Hello from a closed shadow root!</h1>`;   } }

We could also save a reference to the shadow root on our element itself, using a Symbol or other key to try to make the shadow root private.

In general, the closed mode for shadow roots exists for native elements that use the shadow DOM in their implementation (like <audio> or <video>). Further, for unit testing our elements, we might not have access to the shadowRoots object, making it unable for us to target changes inside our element depending on how our library is architected.

There might be some legitimate use cases for user-land closed shadow roots, but they are few and far between, so we’ll stick with the open shadow root for our dialog.

After implementing the new open shadow root, you might notice now that our element is completely broken when we try to run it:

See the Pen
Dialog example using template with shadow root
by Caleb Williams (@calebdwilliams)
on CodePen.

This is because all of the content we had before was added to and manipulated in the traditional DOM (what we’ll call the light DOM). Now that our element has a shadow DOM attached, there is no outlet for the light DOM to render. Let’s start fixing these issues by moving our content to the shadow DOM:

class OneDialog extends HTMLElement {   constructor() {     super();     this.attachShadow({ mode: 'open' });     this.close = this.close.bind(this);   }      connectedCallback() {     const { shadowRoot } = this;     const template = document.getElementById('one-dialog');     const node = document.importNode(template.content, true);     shadowRoot.appendChild(node);          shadowRoot.querySelector('button').addEventListener('click', this.close);     shadowRoot.querySelector('.overlay').addEventListener('click', this.close);     this.open = this.open;   }    disconnectedCallback() {     this.shadowRoot.querySelector('button').removeEventListener('click', this.close);     this.shadowRoot.querySelector('.overlay').removeEventListener('click', this.close);   }      set open(isOpen) {     const { shadowRoot } = this;     shadowRoot.querySelector('.wrapper').classList.toggle('open', isOpen);     shadowRoot.querySelector('.wrapper').setAttribute('aria-hidden', !isOpen);     if (isOpen) {       this._wasFocused = document.activeElement;       this.setAttribute('open', '');       document.addEventListener('keydown', this._watchEscape);       this.focus();       shadowRoot.querySelector('button').focus();     } else {       this._wasFocused && this._wasFocused.focus && this._wasFocused.focus();       this.removeAttribute('open');       document.removeEventListener('keydown', this._watchEscape);     }   }      close() {     this.open = false;   }      _watchEscape(event) {     if (event.key === 'Escape') {         this.close();        }   } }  customElements.define('one-dialog', OneDialog);

The major changes to our dialog so far are actually relatively minimal, but they carry a lot of impact. For starters, all our our selectors (including our style definitions) are internally scoped. For example, our dialog template only has one button internally, so our CSS only targets button { ... }, and those styles don’t bleed out to the light DOM.

We are, however, still reliant on the template that is external to our element. Let’s change that by removing the markup from our template and dropping it into our shadow root’s innerHTML.

See the Pen
Dialog example using only shadow root
by Caleb Williams (@calebdwilliams)
on CodePen.

Including content from the light DOM

The shadow DOM specification includes a means for allowing content from outside the shadow root to be rendered inside of our custom element. For those of you who remember AngularJS, this is a similar concept to ng-transclude or using props.children in React. With Web Components, this is done using the <slot> element.

A simple example would look like this:

<div>   <span>world <!-- this would be inserted into the slot element below --></span>   <#shadow-root><!-- pseudo code -->     <p>Hello <slot></slot></p>   </#shadow-root> </div>

A given shadow root can have any number of slot elements, which can be distinguished with a name attribute. The first slot inside of the shadow root without a name, will be the default slot and all content not otherwise assigned will flow inside that node. Our dialog really needs two slots: a heading and some content (which we’ll make default).

See the Pen
Dialog example using shadow root and slots
by Caleb Williams (@calebdwilliams)
on CodePen.

Go ahead and change the HTML portion of our dialog and see the result. Any content inside of the light DOM is inserted into the slot to which it is assigned. Slotted content remains inside the light DOM although it is rendered as if it were inside the shadow DOM. This means that these elements are still fully style-able by a consumer who might want to control the look and feel of their content.

A shadow root’s author can style content inside the light DOM to a limited extent using the CSS ::slotted() pseudo-selector; however, the DOM tree inside slotted is collapsed, so only simple selectors will work. In other words, we wouldn’t be able to style a <strong> element inside a <p> element within the flattened DOM tree in our previous example.

The best of both worlds

Our dialog is in a good state now: it has encapsulated, semantic markup, styles and behavior; however, some consumers of our dialog might still want to define their own template. Fortunately, by combining two techniques we’ve already learned, we can allow authors to optionally define an external template.

To do this, we will allow each instance of our component to reference an optional template ID. To start, we need to define a getter and setter for our component’s template.

get template() {   return this.getAttribute('template'); }  set template(template) {   if (template) {     this.setAttribute('template', template);   } else {     this.removeAttribute('template');   }   this.render(); }

Here we’re doing much the same thing that we did with our open property by tying it directly to its corresponding attribute. But at the bottom, we’re introducing a new method to our component: render. We are going to use our render method to insert our shadow DOM’s content and remove that behavior from the connectedCallback; instead, we will call render when our element is connected:

connectedCallback() {   this.render(); }  render() {   const { shadowRoot, template } = this;   const templateNode = document.getElementById(template);   shadowRoot.innerHTML = '';   if (templateNode) {     const content = document.importNode(templateNode.content, true);     shadowRoot.appendChild(content);   } else {     shadowRoot.innerHTML = `<!-- template text -->`;   }   shadowRoot.querySelector('button').addEventListener('click', this.close);   shadowRoot.querySelector('.overlay').addEventListener('click', this.close);   this.open = this.open; }

Our dialog now has some really basic default stylings, but also gives consumers the ability to define a new template for each instance. If we wanted, we could even use attributeChangedCallback to make this component update based on the template it’s currently pointing to:

static get observedAttributes() { return 'open', 'template']; }  attributeChangedCallback(attrName, oldValue, newValue) {   if (newValue !== oldValue) {     switch (attrName) {       /** Boolean attributes */       case 'open':         this[attrName] = this.hasAttribute(attrName);         break;       /** Value attributes */       case 'template':         this[attrName] = newValue;         break;     }   } }

See the Pen
Dialog example using shadow root, slots and template
by Caleb Williams (@calebdwilliams)
on CodePen.

In the demo above, changing the template attribute on our <one-dialog> element will alter which design is being used when the element is rendered.

Strategies for styling the shadow DOM

Currently, the only reliable way to style a shadow DOM node is by adding a <style> element to the shadow root’s inner HTML. This works fine in almost every case as browsers will de-duplicate stylesheets across these components, where possible. This does tend to add a bit of memory overhead, but generally not enough to notice.

Inside of these style tags, we can use CSS custom properties to provide an API for styling our components. Custom properties can pierce the shadow boundary and effect content inside a shadow node.

“Can we use a <link> element inside of a shadow root?” you might ask. And, in fact, we can. The trouble comes when trying to reuse this component across multiple applications as the CSS file might not be saved in a consistent location throughout all apps. However, if we are certain as to the element’s stylesheet location, then using <link> is an option. The same holds true for including an @import rule in a style tag.

CSS custom properties

One of the benefits of using CSS custom properties — also called CSS variables — is that they bleed through the shadow DOM. This is by design, giving component authors a surface for allowing theming and styling of their components from the outside. It is important to note, however, that since CSS cascades, changes to custom properties made inside a shadow root do not bleed back up.

See the Pen
CSS custom properties and shadow DOM
by Caleb Williams (@calebdwilliams)
on CodePen.

Go ahead and comment out or remove the variables set in the CSS panel of the demo above and see how this impacts the rendered content. Afterward, you can take a look at the styles in the shadow DOM’s innerHTML, you’ll see how the shadow DOM can define its own property that won’t affect the light DOM.

Constructible stylesheets

At the time of this writing, there is a proposed web feature that will allow for more modular styling of shadow DOM and light DOM elements using constructible stylesheets that has already landed in Chrome 73 and received positive signaling from Mozilla.

This feature would allow authors to define stylesheets in their JavaScript files similar to how they would write normal CSS and share those styles across multiple nodes. So, a single stylesheet could be appended to multiple shadow roots and potentially the document as well.

const everythingTomato = new CSSStyleSheet(); everythingTomato.replace('* { color: tomato; }');  document.adoptedStyleSheets = [everythingTomato];  class SomeCompoent extends HTMLElement {   constructor() {     super();     this.adoptedStyleSheets = [everythingTomato];   }      connectedCallback() {     this.shadowRoot.innerHTML = `<h1>CSS colors are fun</h1>`;   } }

In the above example, the everythingTomato stylesheet would be simultaneously applied to the shadow root and to the document’s body. This feature would be very useful for teams creating design systems and components that are intended to be shared across multiple applications and frameworks.

In the next demo, we can see a really basic example of how this can be utilized and the power that constructble stylesheets offer.

See the Pen
Construct style sheets demo
by Caleb Williams (@calebdwilliams)
on CodePen.

In this demo, we construct two stylesheets and append them to the document and to the custom element. After three seconds, we remove one stylesheet from our shadow root. For those three seconds, however, the document and the shadow DOM share the same stylesheet. Using the polyfill included in that demo, there are actually two style elements present, but Chrome Canary runs this natively.

That demo also includes a form for showing how a sheet’s rules can easily and effectively changed asynchronously as needed. This addition to the web platform can be a powerful ally for those creating design systems that span multiple frameworks or site authors who want to provide themes for their websites.

There is also a proposal for CSS Modules that could eventually be used with the adoptedStyleSheets feature. If implemented in its current form, this proposal would allow importing CSS as a module much like ECMAScript modules:

import styles './styles.css';  class SomeCompoent extends HTMLElement {   constructor() {     super();     this.adoptedStyleSheets = [styles];   } }

Part and theme

Another feature that is in the works for styling Web Components are the ::part() and ::theme() pseudo-selectors. The ::part() specification will allow authors to define parts of their custom elements that have a surface for styling:

class SomeOtherComponent extends HTMLElement {   connectedCallback() {     this.attachShadow({ mode: 'open' });     this.shadowRoot.innerHTML = `       <style>h1 { color: rebeccapurple; }</style>       <h1>Web components are <span part="description">AWESOME</span></h1>     `;   } }      customElements.define('other-component', SomeOtherComponent);

In our global CSS, we could target any element that has a part called description by invoking the CSS ::part() selector.

other-component::part(description) {   color: tomato; }

In the above example, the primary message of the <h1> tag would be in a different color than the description part, giving custom element authors the ability to expose styling APIs for their components and maintain control over the pieces they want to maintain control over.

The difference between ::part() and ::theme() is that ::part() must be specifically selected whereas ::theme() can be nested at any level. The following would have the same effect as the above CSS, but would also work for any other element that included a part="description" in the entire document tree.

:root::theme(description) {   color: tomato; }

Like constructible stylesheets, ::part() has landed in Chrome 73.

Wrapping up

Our dialog component is now complete, more-or-less. It includes its own markup, styles (without any outside dependencies) and behaviors. This component can now be included in projects that use any current or future frameworks because they are built against the browser specifications instead of third-party APIs.

Some of the core controls are a little verbose and do rely on at least a moderate knowledge of how the DOM works. In our final article, we will discuss higher-level tooling and how to incorporate with popular frameworks.

Article Series:

  1. An Introduction to Web Components
  2. Crafting Reusable HTML Templates
  3. Creating a Custom Element from Scratch
  4. Encapsulating Style and Structure with Shadow DOM (This post)
  5. Advanced Tooling for Web Components (Coming soon!)

The post Encapsulating Style and Structure with Shadow DOM appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Extracting Text from Content Using HTML Slot, HTML Template and Shadow DOM

Chapter names in books, quotes from a speech, keywords in an article, stats on a report — these are all types of content that could be helpful to isolate and turn into a high-level summary of what’s important.

For example, have you seen the way Business Insider provides an article’s key points before getting into the content?

That’s the sort of thing we’re going to do, but try to extract the high points directly from the article using HTML Slot, HTML Template and Shadow DOM.

These three titular specifications are typically used as part of Web Components — fully functioning custom element modules meant to be reused in webpages.

Now, what we aim to do, i.e. text extraction, doesn’t need custom elements, but it can make use of those three technologies.

There is a more rudimentary approach to do this. For example, we could extract text and show the extracted text on a page with some basic script without utilizing slot and template. So why use them if we can go with something more familiar?

The reason is that using these technologies permits us a preset markup code (also optionally, style or script) for our extracted text in HTML. We’ll see that as we proceed with this article.

Now, as a very watered-down definition of the technologies we’ll be using, I’d say:

  • A template is a set of markup that can be reused in a page.
  • A slot is a placeholder spot for a designated element from the page.
  • A shadow DOM is a DOM tree that doesn’t really exist on the page till we add it using script.

We’ll see them in a little more depth once we get into coding. For now, what we’re going to make is an article that follows with a list of key points from the text. And, you probably guessed it, those key points are extracted from the article text and compiled into the key points section.

See the Pen
Text Extraction with HTML Slot and HTML Template
by Preethi Sam (@rpsthecoder)
on CodePen.

The key points are displayed as a list with a design in between the points. So, let’s first create a template for that list and designate a place for the list to go.

<article><!-- Article content --></article>  <!-- Section where the extracted keypoints will be displayed --> <section id='keyPointsSection'>   <h2>Key Points:</h2>   <ul><!-- Extracted key points will go in here --></ul> </section>  <!-- Template for the key points list --> <template id='keyPointsTemplate'>   <li><slot name='keyPoints'></slot></li>   <li style="text-align: center;">&#x2919;&mdash;&#x291a;</li> </template>

What we’ve got is a semantic <section> with a <ul> where the list of key points will go. Then we have a <template> for the list items that has two <li> elements: one with a <slot> placeholder for the key points from the article and another with a centered design.

The layout is arbitrary. What’s important is placing a <slot> where the extracted key points will go. Whatever’s inside the <template> will not be rendered on the page until we add it to the page using script.

Further, the markup inside <template> can be styled using inline styles, or CSS enclosed by <style>:

<template id='keyPointsTemplate'>     <li><slot name='keyPoints'></slot></li>     <li style="text-align: center;">&#x2919;&mdash;&#x291a;</li>     <style>         li{/* Some style */}     </style> </template>

The fun part! Let’s pick the key points from the article. Notice the value of the name attribute for the <slot> inside the <template> (keyPoints) because we’ll need that.

<article>   <h1>Bears</h1>   <p>Bears are carnivoran mammals of the family Ursidae. <span><span slot='keyPoints'>They are classified as caniforms, or doglike carnivorans</span></span>. Although only eight species of bears <!-- more content --> and partially in the Southern Hemisphere. <span><span slot='keyPoints'>Bears are found on the continents of North America, South America, Europe, and Asia</span></span>.<!-- more content --></p>   <p>While the polar bear is mostly carnivorous, <!-- more content -->. Bears use shelters, such as caves and logs, as their dens; <span><span slot='keyPoints'>Most species occupy their dens during the winter for a long period of hibernation</span></span>, up to 100 days.</p>   <!-- More paragraphs -->  </article>

The key points are wrapped in a <span> carrying a slot attribute value (“keyPoints“) matching the name of the <slot> placeholder inside the <template>.

Notice, too, that I’ve added another outer <span> wrapping the key points.

The reason is that slot names are usually unique and are not repeated, because one <slot> matches one element using one slot name. If there’re more than one element with the same slot name, the <slot> placeholder will be replaced by all those elements consecutively, ending in the last element being the final content at the placeholder.

So, if we matched that one single <slot> inside the <template> against all of the <span> elements with the same slot attribute value (our key points) in a paragraph or the whole article, we’d end up with only the last key point present in the paragraph or the article in place of the <slot>.

That’s not what we need. We need to show all the key points. So, we’re wrapping the key points with an outer <span> to match each of those individual key points separately with the <slot>. This is much more obvious by looking at the script, so let’s do that.

const keyPointsTemplate = document.querySelector('#keyPointsTemplate').content; const keyPointsSection = document.querySelector('#keyPointsSection > ul'); /* Loop through elements with 'slot' attribute */ document.querySelectorAll('[slot]').forEach((slot)=>{   let span = slot.parentNode.cloneNode(true);   span.attachShadow({  mode: 'closed' }).appendChild(keyPointsTemplate.cloneNode(true));   keyPointsSection.appendChild(span); });

First, we loop through every <span> with a slot attribute and get a copy of its parent (the outer <span>). Note that we could also loop through the outer <span> directly if we’d like, by giving them a common class value.

The outer <span> copy is then attached with a shadow tree (span.attachShadow) made up of a clone of the template’s content (keyPointsTemplate.cloneNode(true)).

This “attachment” causes the <slot> inside the template’s list item in the shadow tree to absorb the inner <span> carrying its matching slot name, i.e. our key point.

The slotted key point is then added to the key points section at the end of the page (keyPointsSection.appendChild(span)).

This happens with all the key points in the course of the loop.

That’s really about it. We’ve snagged all of the key points in the article, made copies of them, then dropped the copies into the list template so that all of the key points are grouped together providing a nice little CliffsNotes-like summary of the article.

Here’s that demo once again:

See the Pen
Text Extraction with HTML Slot and HTML Template
by Preethi Sam (@rpsthecoder)
on CodePen.

What do you think of this technique? Is it something that would be useful in long-form content, like blog posts, news articles, or even Wikipedia entries? What other use cases can you think of?

The post Extracting Text from Content Using HTML Slot, HTML Template and Shadow DOM appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , , ,
[Top]