├── .github
├── CODEOWNERS
├── PULL_REQUEST_TEMPLATE
├── labels.json
└── workflows
│ ├── codeql-analysis.yml
│ └── set-default-labels.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composed-composed-path
├── index.html
└── main.js
├── defined-pseudo-class
└── index.html
├── edit-word
├── index.html
└── main.js
├── editable-list
├── index.html
├── main.js
└── style.css
├── element-details
├── index.html
└── main.js
├── expanding-list-web-component
├── img
│ ├── down.png
│ └── right.png
├── index.html
└── main.js
├── host-selectors
├── index.html
├── main.js
└── styles.css
├── life-cycle-callbacks
├── index.html
└── main.js
├── popup-info-box-external-stylesheet
├── img
│ ├── alt.png
│ └── default.png
├── index.html
├── main.js
└── style.css
├── popup-info-box-web-component
├── img
│ ├── alt.png
│ └── default.png
├── index.html
└── main.js
├── shadow-part
├── index.html
├── main.js
└── style.css
├── simple-template
├── index.html
└── main.js
├── slotchange
├── index.html
└── main.js
├── slotted-pseudo-element
├── index.html
└── main.js
└── word-count-web-component
├── index.html
└── main.js
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # This file is used to request PR reviews from the appropriate team.
2 |
3 | # Default
4 | * @mdn/core-yari-content
5 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE:
--------------------------------------------------------------------------------
1 |
2 | #### Summary
3 |
4 |
5 | #### Supporting details
6 |
7 |
8 | #### Related issues
9 |
10 |
11 | #### Related content pull request
12 |
13 |
--------------------------------------------------------------------------------
/.github/labels.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "good first issue",
4 | "color": "028c46",
5 | "description": "A good issue for newcomers to get started with."
6 | },
7 | {
8 | "name": "help wanted",
9 | "color": "028c46",
10 | "description": "If you know something about this, we would love your help!"
11 | },
12 | {
13 | "name": "needs info",
14 | "color": "028c46",
15 | "description": "This needs more information to review or act on."
16 | },
17 | {
18 | "name": "needs triage",
19 | "color": "028c46",
20 | "description": "Triage needed by staff and/or partners. Automatically applied when an issue is opened."
21 | },
22 | {
23 | "name": "expert help needed",
24 | "color": "028c46",
25 | "description": "This needs more information from a subject matter expert (SME)."
26 | },
27 | {
28 | "name": "idle",
29 | "color": "028c46",
30 | "description": "Issues and pull requests with no activity for three months."
31 | },
32 | {
33 | "name": "on hold",
34 | "color": "028c46",
35 | "description": "Waiting on something else before this can be moved forward."
36 | },
37 | {
38 | "name": "for later",
39 | "color": "028c46",
40 | "description": "Not planned at this time."
41 | },
42 | {
43 | "name": "needs content update",
44 | "color": "028c46",
45 | "description": "Needs update to the content to support this change."
46 | },
47 | {
48 | "name": "chore",
49 | "color": "028c46",
50 | "description": "A routine task."
51 | },
52 | {
53 | "name": "enhancement",
54 | "color": "028c46",
55 | "description": "Improves an existing feature."
56 | },
57 | {
58 | "name": "bug",
59 | "color": "c05964",
60 | "description": "Indicates an unexpected problem or unintended behavior."
61 | },
62 | {
63 | "name": "wontfix",
64 | "color": "c05964",
65 | "description": "Deemed to be outside the scope of the project or would require significant time and resources to fix."
66 | },
67 | {
68 | "name": "effort: small",
69 | "color": "866dc1",
70 | "description": "Task is a small effort."
71 | },
72 | {
73 | "name": "effort: medium",
74 | "color": "866dc1",
75 | "description": "Task is a medium effort."
76 | },
77 | {
78 | "name": "effort: large",
79 | "color": "866dc1",
80 | "description": "Task is large effort."
81 | },
82 | {
83 | "name": "p0",
84 | "color": "6e8bc1",
85 | "description": "Urgent. We will address this as soon as possible."
86 | },
87 | {
88 | "name": "p1",
89 | "color": "6e8bc1",
90 | "description": "We will address this soon and will provide capacity from our team for it in the next few releases."
91 | },
92 | {
93 | "name": "p2",
94 | "color": "6e8bc1",
95 | "description": "We want to address this but may have other higher priority items."
96 | },
97 | {
98 | "name": "p3",
99 | "color": "6e8bc1",
100 | "description": "We don't have visibility when this will be addressed."
101 | }
102 | ]
103 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | # The branches below must be a subset of the branches above
8 | branches: [ main ]
9 | schedule:
10 | - cron: '42 14 * * 3'
11 |
12 | jobs:
13 | analyze:
14 | name: Analyze
15 | runs-on: ubuntu-latest
16 | permissions:
17 | actions: read
18 | contents: read
19 | security-events: write
20 |
21 | strategy:
22 | fail-fast: false
23 | matrix:
24 | language: [ 'javascript' ]
25 |
26 | steps:
27 | - name: Checkout repository
28 | uses: actions/checkout@v3
29 |
30 | # Initializes the CodeQL tools for scanning.
31 | - name: Initialize CodeQL
32 | uses: github/codeql-action/init@v2
33 | with:
34 | languages: ${{ matrix.language }}
35 |
36 | - name: Perform CodeQL Analysis
37 | uses: github/codeql-action/analyze@v2
38 |
--------------------------------------------------------------------------------
/.github/workflows/set-default-labels.yml:
--------------------------------------------------------------------------------
1 | name: set-default-labels
2 | on: [workflow_dispatch]
3 |
4 | jobs:
5 | set-default-labels:
6 | uses: mdn/workflows/.github/workflows/set-default-labels.yml@main
7 | with:
8 | target-repo: "mdn/web-components-examples"
9 | should-delete-labels: true
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Community Participation Guidelines
2 |
3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines.
4 | For more details, please read the
5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
6 |
7 | ## How to Report
8 | For more information on how to report violations of the Community Participation Guidelines, please read our [How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/) page.
9 |
10 |
16 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribute
2 |
3 | Welcome to the contributing guide for [mdn/web-components-examples](https://github.com/mdn/web-components-examples).
4 |
5 | Thank you for your interest in contributing to this repository. By contributing, you agree to abide by our [community guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/).
6 |
7 | To contribute, please open a pull request. Please make sure any updates reflect the latest version of the relevant specification(s).
8 |
9 | Please note that this repository only contains examples that are documented on MDN Web Docs. For pull requests that update code and/or demo examples (such as in this repository), we require an accompanying pull request on the [mdn/content repository](https://github.com/mdn/content) updating the relevant documentation.
10 |
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
117 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # web-components-examples
2 | A series of Web Components examples, related to the MDN Web Components documentation at https://developer.mozilla.org/en-US/docs/Web/API/Web_components.
3 |
4 | > Please refer to our [contribution guidelines](https://github.com/mdn/web-components-examples/blob/main/CONTRIBUTING.md) before contributing.
5 |
6 | The following examples are available:
7 |
8 | * [composed-composed-path](composed-composed-path). A very simple example that shows the behavior of the Event object composed and composedPath properties. [See composed-composed-path live](https://mdn.github.io/web-components-examples/composed-composed-path/).
9 | * [defined-pseudo-class](defined-pseudo-class). A very simple example that shows how the [:defined pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:defined) works. [See defined-pseudo-class live](https://mdn.github.io/web-components-examples/defined-pseudo-class/).
10 | * [editable-list](editable-list) – <editable-list>. A simple example showing how elements can be consolidated to create a list with addable/removable items. Items are added by using a `list-item` attribute or by entering text and clicking the plus sign. [See editable-list live](https://mdn.github.io/web-components-examples/editable-list/).
11 | * [edit-word](edit-word) — <edit-word>. Wrapping one or more words in this element means that you can then click/focus the element to reveal a text input that can then be used to edit the word(s). [See edit-word live](https://mdn.github.io/web-components-examples/edit-word/).
12 | * [element-details](element-details) — <element-details>. Displays a box containing an HTML element name and description. Provides an example of an autonomous custom element that gets its structure from a <template> element (that also has its own styling defined), and also contains <slot> elements populated at runtime. [See element-details live](https://mdn.github.io/web-components-examples/element-details/).
13 | * [expanding-list-web-component](expanding-list-web-component) — <ul is="expanding-list">. Creates an unordered list with expandable/collapsible children. Provides an example of a customized built-in element (the class inherits from HTMLUListElement rather than HTMLElement). [See expanding-list live](https://mdn.github.io/web-components-examples/expanding-list-web-component/).
14 | * [life-cycle-callbacks](life-cycle-callbacks) — <custom-square l="" c="">. A trivial example web component that creates a square colored box on the page. The demo also includes buttons to create, destroy, and change attributes on the element, to demonstrate how the [web components life cycle callbacks](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#Using_the_lifecycle_callbacks) work [See life-cycle-callbacks live](https://mdn.github.io/web-components-examples/life-cycle-callbacks/).
15 | * [popup-info-box-web-component](popup-info-box-web-component) — <popup-info img="" text="">. Creates an info icon that when focused displays a popup info box. Provides an example of an autonomous custom element that takes information from its attributes, and defines structure and basic style in an attached shadow DOM. [See popup-info-box live](https://mdn.github.io/web-components-examples/popup-info-box-web-component/).
16 | * [simple-template](simple-template) — A very simple trivial example that quickly demonstrates usage of the <template> and <slot> elements. [See simple-template live](https://mdn.github.io/web-components-examples/simple-template/).
17 | * [slotchange example](slotchange) — <summary-display>. An example that takes as its two slot values a list of possible choices, and a description for the selected choice. Multiple paragraphs are included inside the element containing all the possible descriptions; when a choice is clicked, its corresponding description paragraph is given an appropriate slot attribute so that it appears in the second slot. This example is written to demonstrate usage of the slotchange attribute, and features of the HTMLSlotElement interface [See the slotchange example live](https://mdn.github.io/web-components-examples/slotchange).
18 | * [slotted-pseudo-element](slotted-pseudo-element). A very simple example that shows how the ::slotted pseudo-element works. [See slotted-pseudo-element live](https://mdn.github.io/web-components-examples/slotted-pseudo-element/).
19 | * [word-count-web-component](word-count-web-component) — <word-count>. When added to an element, counts all the words inside that element and displays them inside an attached shadow DOM. It also contains an interval that periodically updates the word count as it changes. Provides an example of a customized built-in element (the class inherits from HTMLParagraphElement rather than HTMLElement). [See word-count live](https://mdn.github.io/web-components-examples/word-count-web-component/).
20 |
--------------------------------------------------------------------------------
/composed-composed-path/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
38 |
39 |
40 |
41 |
42 |
43 | slot
44 | A placeholder inside a web
45 | component that users can fill with their own markup,
46 | with the effect of composing different DOM trees
47 | together.
48 |
49 |
name
50 |
The name of the slot.
51 |
52 |
53 |
54 |
55 | template
56 | A mechanism for holding client-
57 | side content that is not to be rendered when a page is
58 | loaded but may subsequently be instantiated during
59 | runtime using JavaScript.
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/element-details/main.js:
--------------------------------------------------------------------------------
1 | customElements.define('element-details',
2 | class extends HTMLElement {
3 | constructor() {
4 | super();
5 | const template = document
6 | .getElementById('element-details-template')
7 | .content;
8 | const shadowRoot = this.attachShadow({mode: 'open'})
9 | .appendChild(template.cloneNode(true));
10 | }
11 | });
12 |
--------------------------------------------------------------------------------
/expanding-list-web-component/img/down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/web-components-examples/16c91cd0486afdd6407c2d606742a7fbf54c79f2/expanding-list-web-component/img/down.png
--------------------------------------------------------------------------------
/expanding-list-web-component/img/right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/web-components-examples/16c91cd0486afdd6407c2d606742a7fbf54c79f2/expanding-list-web-component/img/right.png
--------------------------------------------------------------------------------
/expanding-list-web-component/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Expanding list web component
6 |
40 |
41 |
42 |
43 |
Expanding list web component
44 |
45 |
46 | UK
47 |
48 |
49 | Yorkshire
50 |
51 |
52 | Leeds
53 |
54 |
Train station
55 |
Town hall
56 |
Headrow
57 |
58 |
59 |
Bradford
60 |
Hull
61 |
62 |
63 |
64 |
65 |
66 | USA
67 |
68 |
69 | California
70 |
71 |
Los Angeles
72 |
San Francisco
73 |
Berkeley
74 |
75 |
76 |
Nevada
77 |
Oregon
78 |
79 |
80 |
81 |
82 |
83 |
Not
84 |
an
85 |
expanding
86 |
list
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/expanding-list-web-component/main.js:
--------------------------------------------------------------------------------
1 | // Create a class for the element
2 | class ExpandingList extends HTMLUListElement {
3 | constructor() {
4 | // Always call super first in constructor
5 | // Return value from super() is a reference to this element
6 | self = super();
7 | }
8 |
9 | connectedCallback() {
10 | // Get ul and li elements that are a child of this custom ul element
11 | // li elements can be containers if they have uls within them
12 | const uls = Array.from(self.querySelectorAll("ul"));
13 | const lis = Array.from(self.querySelectorAll("li"));
14 | // Hide all child uls
15 | // These lists will be shown when the user clicks a higher level container
16 | uls.forEach((ul) => {
17 | ul.style.display = "none";
18 | });
19 |
20 | // Look through each li element in the ul
21 | lis.forEach((li) => {
22 | // If this li has a ul as a child, decorate it and add a click handler
23 | if (li.querySelectorAll("ul").length > 0) {
24 | // Add an attribute which can be used by the style
25 | // to show an open or closed icon
26 | li.setAttribute("class", "closed");
27 |
28 | // Wrap the li element's text in a new span element
29 | // so we can assign style and event handlers to the span
30 | const childText = li.childNodes[0];
31 | const newSpan = document.createElement("span");
32 |
33 | // Copy text from li to span, set cursor style
34 | newSpan.textContent = childText.textContent;
35 | newSpan.style.cursor = "pointer";
36 |
37 | // Add click handler to this span
38 | newSpan.addEventListener("click", (e) => {
39 | // next sibling to the span should be the ul
40 | const nextul = e.target.nextElementSibling;
41 |
42 | // Toggle visible state and update class attribute on ul
43 | if (nextul.style.display == "block") {
44 | nextul.style.display = "none";
45 | nextul.parentNode.setAttribute("class", "closed");
46 | } else {
47 | nextul.style.display = "block";
48 | nextul.parentNode.setAttribute("class", "open");
49 | }
50 | });
51 | // Add the span and remove the bare text node from the li
52 | childText.parentNode.insertBefore(newSpan, childText);
53 | childText.parentNode.removeChild(childText);
54 | }
55 | });
56 | }
57 | }
58 |
59 | // Define the new element
60 | customElements.define("expanding-list", ExpandingList, { extends: "ul" });
61 |
--------------------------------------------------------------------------------
/host-selectors/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Host selectors
6 |
7 |
8 |
9 |
10 |
11 |
This article is rather lovely and exciting — it is all about animals, including Beavers, Bears, and Wolves. I love animals and I'm sure you will too; please let us know what your favorite animals are. Woo hoo!
18 |
19 |
20 |
21 |
22 |
This is my second article.
23 |
24 |
This article is also quite exciting — it is all about colors, including Red, Blue, and Pink. A true joy indeed — funky exciting colors make the world go round. No more gray days for us.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pulvinar sed justo sed viverra. Aliquam ac scelerisque tellus. Vivamus porttitor nunc vel nibh rutrum hendrerit. Donec viverra vestibulum pretium. Mauris at eros vitae ante pellentesque bibendum. Etiam et blandit purus, nec aliquam libero. Etiam leo felis, pulvinar et diam id, sagittis pulvinar diam. Nunc pellentesque rutrum sapien, sed faucibus urna sodales in. Sed tortor nisl, egestas nec egestas luctus, faucibus vitae purus. Ut elit nunc, pretium eget fermentum id, accumsan et velit. Sed mattis velit diam, a elementum nunc facilisis sit amet.
14 |
15 |
Pellentesque ornare tellus sit amet massa tincidunt congue. Morbi cursus, tellus vitae pulvinar dictum, dui turpis faucibus ipsum, nec hendrerit augue nisi et enim. Curabitur felis metus, euismod et augue et, luctus dignissim metus. Mauris placerat tellus id efficitur ornare. Cras enim urna, vestibulum vel molestie vitae, mollis vitae eros. Sed lacinia scelerisque diam, a varius urna iaculis ut. Nam lacinia, velit consequat venenatis pellentesque, leo tortor porttitor est, sit amet accumsan ex lectus eget ipsum. Quisque luctus, ex ac fringilla tincidunt, risus mauris sagittis mauris, at iaculis mauris purus eget neque. Donec viverra in ex sed ullamcorper. In ac nisi vel enim accumsan feugiat et sed augue. Donec nisl metus, sollicitudin eu tempus a, scelerisque sed diam.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/word-count-web-component/main.js:
--------------------------------------------------------------------------------
1 | // Create a class for the element
2 | class WordCount extends HTMLParagraphElement {
3 | constructor() {
4 | // Always call super first in constructor
5 | super();
6 |
7 | // count words in element's parent element
8 | const wcParent = this.parentNode;
9 |
10 | function countWords(node){
11 | const text = node.innerText || node.textContent;
12 | return text.trim().split(/\s+/g).filter(a => a.trim().length > 0).length;
13 | }
14 |
15 | const count = `Words: ${countWords(wcParent)}`;
16 |
17 | // Create a shadow root
18 | const shadow = this.attachShadow({mode: 'open'});
19 |
20 | // Create text node and add word count to it
21 | const text = document.createElement('span');
22 | text.textContent = count;
23 |
24 | // Append it to the shadow root
25 | shadow.appendChild(text);
26 |
27 | // Update count when element content changes
28 | this.parentNode.addEventListener('input', () => {
29 | text.textContent = `Words: ${countWords(wcParent)}`;
30 | });
31 | }
32 | }
33 |
34 | // Define the new element
35 | customElements.define('word-count', WordCount, { extends: 'p' });
36 |
--------------------------------------------------------------------------------