├── .gitignore
├── package.json
├── README.md
├── read-time.js
└── demo
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | components
2 | bower_components
3 | node_modules
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "read-time-wc",
3 | "main": "read-time.js",
4 | "version": "3.0.0",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/matthewp/read-time.git"
8 | },
9 | "author": "",
10 | "license": "BSD-2-Clause",
11 | "bugs": {
12 | "url": "https://github.com/matthewp/read-time/issues"
13 | },
14 | "homepage": "https://github.com/matthewp/read-time#readme",
15 | "files": [
16 | "read-time.js"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # <read-time>
2 |
3 | [](https://www.webcomponents.org/element/matthewp/read-time)
4 | [](https://www.npmjs.com/package/read-time-wc)
5 |
6 | A component for calculating how long it will take to read an article.
7 |
8 | 
9 |
10 | ## Install
11 |
12 | Install with npm or yarn:
13 |
14 | ```shell
15 | yarn add read-time-wc
16 | ```
17 |
18 | Import into your page
19 |
20 | ```html
21 |
22 | ```
23 |
24 | ## Usage
25 |
26 | `` takes a `selector` that contains the article you want to calculate the read time for. For example:
27 |
28 | ```html
29 |
30 |
31 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam commodo lorem sit amet fringilla interdum. Morbi imperdiet eros id ante tempus commodo. Fusce pretium metus tortor, a lacinia nulla tincidunt nec. Vestibulum placerat consequat ipsum, interdum fringilla lectus malesuada eget. Cras vitae dui luctus lectus viverra eleifend in sed sapien. Quisque sit amet elit ligula. Vestibulum facilisis pretium libero ultricies vehicula. Donec arcu diam, rhoncus sit amet purus et, porttitor auctor quam. Ut nec metus eget enim iaculis mollis non non arcu.
32 | ```
33 |
34 | ## Attributes
35 |
36 | ### Selector
37 |
38 | A CSS selector of the article who's read time will be calculated. Can be any selector that can be found by `querySelector`.
39 |
40 | This field is **required**.
41 |
42 | ```html
43 |
44 | ```
45 |
46 | ### WPM
47 |
48 | The number of words-per-minute of the reader. Defaults to `200`. If your audience is faster/slower, adjust this attribute to get a more accurate read time.
49 |
50 | ```html
51 |
52 | ```
53 |
--------------------------------------------------------------------------------
/read-time.js:
--------------------------------------------------------------------------------
1 | const template = document.createElement('template');
2 | template.innerHTML = /* html */ `
3 |
8 |
9 |
10 | `;
11 |
12 | function clone() {
13 | return document.importNode(template.content, true);
14 | }
15 |
16 | class ReadTime extends HTMLElement {
17 | static get observedAttributes() {
18 | return ['selector', 'wpm'];
19 | }
20 |
21 | constructor() {
22 | super();
23 | this._selector = this.getAttribute('selector') || null;
24 | this._wpm = Number(this.getAttribute('wpm') || 200);
25 | this._connected = false;
26 | this._root = null;
27 | this.attachShadow({ mode: 'open' });
28 | }
29 |
30 | connectedCallback() {
31 | if(!this._connected) {
32 | this.shadowRoot.appendChild(clone());
33 | this._root = this.shadowRoot.getElementById('root');
34 | }
35 |
36 | this._connected = true;
37 | this._setTime();
38 | if(!this._selectedElement()) {
39 | let win = this.ownerDocument.defaultView;
40 | win.addEventListener('load', function onload(){
41 | win.removeEventListener('load', onload);
42 | this._setTime();
43 | }.bind(this));
44 | }
45 | }
46 |
47 | attributeChangedCallback(name, oldValue, newValue) {
48 | switch(name) {
49 | case 'selector':
50 | this.selector = newValue;
51 | break;
52 | case 'wpm':
53 | this.wpm = newValue;
54 | break;
55 | }
56 | }
57 |
58 | /**
59 | * CSS selector for content
60 | */
61 | get selector() {
62 | return this._selector;
63 | }
64 |
65 | set selector(val) {
66 | val = String(val);
67 | if(this._selector === val) return;
68 | this._selector = val;
69 | if(this.hasAttribute('selector')) {
70 | this.setAttribute('selector', val);
71 | }
72 | this._setTime();
73 | }
74 |
75 | /**
76 | * Words per minute
77 | * Used to calculate the time it will
78 | * take to read the article
79 | */
80 | get wpm() {
81 | return this._wpm;
82 | }
83 |
84 | set wpm(val) {
85 | val = Number(val);
86 | if(this._wpm === val) return;
87 | this._wpm = val;
88 | if(this.hasAttribute('wpm')) {
89 | this.setAttribute('wpm', val);
90 | }
91 | this._setTime();
92 | }
93 |
94 | /**
95 | * The time (in minutes) it will take to read the article
96 | */
97 | get time() {
98 | return Math.ceil(this.words / this.wpm);
99 | }
100 |
101 | /**
102 | * The number of words contained within the article
103 | */
104 | get words() {
105 | let element = this._selectedElement();
106 | if(!element) {
107 | return 0;
108 | }
109 | let text = element.textContent;
110 | let words = text.split(' ').length;
111 | return words;
112 | }
113 |
114 | _selectedElement() {
115 | return this.ownerDocument.querySelector(this.selector);
116 | }
117 |
118 | _setTime() {
119 | if(this._connected) {
120 | this._root.textContent = this.time;
121 | }
122 | }
123 | }
124 |
125 | export default ReadTime;
126 |
127 | customElements.define('read-time', ReadTime);
128 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <read-time> demo
7 |
8 |
9 |
14 |
15 |
16 |
17 | Normal
18 | min read
19 |
20 |
21 | 400 WPM
22 | min read
23 |
24 |
25 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam commodo lorem sit amet fringilla interdum. Morbi imperdiet eros id ante tempus commodo. Fusce pretium metus tortor, a lacinia nulla tincidunt nec. Vestibulum placerat consequat ipsum, interdum fringilla lectus malesuada eget. Cras vitae dui luctus lectus viverra eleifend in sed sapien. Quisque sit amet elit ligula. Vestibulum facilisis pretium libero ultricies vehicula. Donec arcu diam, rhoncus sit amet purus et, porttitor auctor quam. Ut nec metus eget enim iaculis mollis non non arcu.
26 |
27 | Aenean id ipsum lacus. Nunc ac commodo est, ut mattis risus. Donec et ultricies risus. Integer dui orci, vestibulum ac accumsan at, fermentum ac urna. Nam imperdiet, turpis in posuere tempor, ante eros pellentesque ipsum, a dapibus nisl lacus sit amet velit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin sit amet laoreet neque. Curabitur ut nibh egestas, elementum magna vitae, malesuada quam. Cras massa mi, adipiscing vitae vulputate nec, lacinia non ligula. Cras venenatis ut purus et rutrum. Aenean velit dui, fermentum id imperdiet id, vehicula ut enim. Mauris vestibulum diam ut aliquet sollicitudin. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
28 |
29 | Fusce dictum, purus viverra placerat malesuada, purus ipsum iaculis felis, vel interdum diam nulla eget justo. Vivamus odio libero, placerat ut iaculis in, sodales ut lectus. Curabitur nec felis in neque gravida viverra. Morbi at fermentum ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin luctus, sapien sit amet tincidunt iaculis, nulla massa porta mauris, eu fermentum orci massa id dui. Nullam eu augue condimentum, pulvinar orci at, malesuada diam. Quisque sit amet dolor placerat, porta metus ac, viverra velit.
30 |
31 | Nullam consequat suscipit magna ac feugiat. Phasellus neque arcu, hendrerit sed feugiat et, consectetur nec augue. Proin semper sem est, ac rhoncus augue dapibus quis. Sed volutpat, libero eu sagittis bibendum, nulla nisi lobortis nibh, bibendum sagittis nibh nisl id leo. Morbi sit amet arcu elementum, vulputate est vitae, varius massa. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla at vehicula nisi. Nullam in ante malesuada, gravida ligula sed, pretium ante. Nam porttitor pellentesque dui, in dignissim tellus commodo ut. Cras vulputate aliquam lacinia. Vestibulum euismod erat at rhoncus blandit.
32 |
33 | Maecenas sapien ante, facilisis a pretium a, volutpat vel nibh. Morbi dignissim sollicitudin tellus in pretium. Cras sed tincidunt nisl. Sed sed sem quis felis venenatis dictum. Curabitur sodales quam id ante fermentum dapibus. Morbi ante magna, lacinia vel diam vitae, luctus scelerisque nibh. Nam sed auctor odio.
34 |
35 | Donec sodales arcu ut dolor malesuada, eget suscipit orci fermentum. Curabitur luctus erat magna, et egestas orci condimentum id. Nulla ultricies nisi non ipsum dictum, scelerisque scelerisque diam varius. Nulla est eros, sollicitudin consequat elementum eu, ornare vitae tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ante orci, vulputate sit amet ligula vulputate, mollis volutpat sapien. Nulla tortor justo, laoreet eget massa eu, sodales lobortis nunc. Nullam turpis mi, mattis ut rutrum non, ultrices a nunc. Ut varius nibh mi, in pharetra augue mollis ac. Vestibulum pellentesque porta cursus. Sed commodo enim eu aliquet lacinia. Aliquam erat purus, faucibus ut lectus eget, mattis ultricies risus. Suspendisse felis leo, molestie quis nunc a, facilisis elementum augue. Mauris at ornare leo, id tempus leo. Morbi cursus enim tortor.
36 |
37 | Nunc dui odio, dignissim eget dictum nec, laoreet at diam. Nam eget porttitor risus. Donec est metus, volutpat at pretium sit amet, volutpat eget risus. Curabitur sed scelerisque diam. Aliquam lorem erat, tincidunt dapibus feugiat sed, hendrerit at lacus. In quis vestibulum dolor, at porta enim. Nullam eget metus nisi. Vivamus enim quam, interdum vitae quam ut, scelerisque fringilla elit. Vestibulum ut nisi nec massa pulvinar laoreet dapibus at ligula. Fusce pellentesque pharetra dolor vitae volutpat. Aenean eu sollicitudin elit. Mauris pulvinar nulla eleifend sem ullamcorper tincidunt. Cras non orci nec lacus porttitor mattis non a dui. Maecenas quis dictum turpis. Integer luctus tellus nec est sollicitudin, eu ultricies mi ultricies.
38 |
39 | Integer ante velit, ornare hendrerit porta hendrerit, porttitor quis neque. Ut lacinia, arcu at tempor ultrices, lectus purus facilisis libero, nec eleifend massa libero in purus. Nullam fringilla eu metus eu tristique. Nulla felis tortor, elementum ut ipsum non, auctor hendrerit dui. In hac habitasse platea dictumst. Maecenas non nisi tempus, egestas nisi eu, adipiscing nulla. Ut auctor eu ante eget vestibulum. Suspendisse potenti. Curabitur felis sem, elementum pulvinar nisi sit amet, vehicula volutpat odio.
40 |
41 | Mauris ut porta dui, nec suscipit lacus. Curabitur vel elit auctor, porta diam vel, varius lorem. Suspendisse leo sem, iaculis vel elementum in, fringilla ultrices leo. Fusce elementum mollis elit, at scelerisque risus aliquet quis. Suspendisse id placerat arcu, non auctor est. Mauris eu justo orci. Cras sodales augue ac vulputate lacinia. Nunc eu libero nisl.
42 |
43 | Nullam malesuada sit amet diam rhoncus vulputate. Nulla vehicula tincidunt pharetra. Nunc eleifend id purus nec adipiscing. Cras libero eros, malesuada vitae mauris at, porta pretium quam. Mauris ut nibh vitae orci viverra lacinia. Pellentesque eget dolor sit amet purus ultrices hendrerit. Suspendisse potenti. Maecenas ut tempor risus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus at blandit tellus. Cras sagittis est vel nunc tempor, in interdum massa porta. Maecenas sed hendrerit diam, non imperdiet mauris. Quisque velit dolor, aliquet ac augue id, tristique tincidunt lacus.
44 |
45 | Morbi rutrum eu tortor et congue. Curabitur ultrices faucibus massa, ut porta ligula sollicitudin vitae. Integer tristique euismod leo mattis placerat. Vivamus suscipit, libero at varius euismod, sapien nulla mattis purus, at interdum ipsum odio sed enim. Mauris interdum eget diam at lobortis. Donec et lorem vel enim ullamcorper varius in ut velit. Praesent sollicitudin sodales nibh, id laoreet ante aliquet sed. Aenean fermentum commodo enim, et eleifend orci accumsan sed. Donec consequat aliquet orci, quis ornare nisl pellentesque at. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc ultricies, ante non blandit mollis, orci ante pulvinar nisi, eget varius magna nibh sit amet sapien. Nam egestas euismod risus, ut iaculis diam euismod.
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------