Skip to content

Commit a1aa1a7

Browse files
authored
Merge pull request #4 from sashafirsov/develop
rev 1.0.2
2 parents 4fb1d8b + de7b8ca commit a1aa1a7

File tree

8 files changed

+180
-40
lines changed

8 files changed

+180
-40
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
2+
package-lock.json
23

34
# Logs
45
logs

README.md

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ are covering the typical UI tasks:
66
3. control UI parts(slots) depending on fetch state.
77

88
[![git](https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg) GitHub](https://github.com/sashafirsov/slotted-element)
9-
| Demo: [slotted-element](https://unpkg.com/[email protected].1/demo/index.html)
10-
, [fetch-element JSON as table](https://unpkg.com/[email protected].1/demo/render-from-json.html)
9+
| Demo: [slotted-element](https://unpkg.com/[email protected].2/demo/index.html)
10+
, [fetch-element JSON as table](https://unpkg.com/[email protected].2/demo/render-from-json.html)
1111
| [tests project](https://github.com/sashafirsov/slotted-element-test)
1212

1313
[![NPM version][npm-image]][npm-url]
@@ -23,7 +23,7 @@ the `slotted-element` is derived from `fetch-element`.
2323
2. Import into page/module either by ES6 import or simple SCRIPT tag
2424
3. In page body add ```<fetch-element src="url/to/some.html"></fetch-element>``` or
2525

26-
```
26+
```html
2727
<slotted-element src="url/to/some.json">
2828
<i slot="loading"> Loading... please wait. </i>
2929
<i slot="errror"> System error, please try again. </i>
@@ -36,19 +36,33 @@ the `slotted-element` is derived from `fetch-element`.
3636
![screenshot][screenshot]
3737

3838
# slotted-element
39-
Gives ability to use slots **without shadowDOM** and exposes API to work with slots programmatically by adding and
40-
removing slots by slot name.
39+
Gives ability to use slots
40+
**without [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM)**
41+
and exposes API to work with slots programmatically by adding and removing slots by slot name.
4142

42-
Coupled with `fetch-element` it provides UI management for each stage of data fetch and transforming to UI.
43+
Coupled with `fetch-element`, it provides UI management for each stage of data fetch and UI transformation.
4344

4445
The slots concept is described in
4546
[using slots in MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots#adding_flexibility_with_slots)
4647

47-
Originally it works in conjunction with shadowDOM when slots defined in content of element and referenced in
48-
rendered shadowDOM by name. I.e. rendered DOM defines which slot and where will be displayed inside of web component.
48+
Originally slots are designed to work in conjunction with template and shadowDOM when slot values are defined in content of
49+
element and referenced in rendered shadowDOM by name. I.e. template DOM defines which slot and where will be displayed
50+
inside of web component.
51+
52+
## Template vs inline HTML
53+
If the `template` is defined as getter method, property, or attribute then
54+
55+
slotted-element simulates the usual ShadowDOM slots handling by template cloning into local DOM and
56+
placing the slots from inner DOM into template clone. Unlike in ShadowDOM this is less efficient as template DOM
57+
is not reused and inner DOM has to be re-build.
58+
59+
Without `template` property defined the inner content is uses as template:
60+
61+
inner DOM is shown except of elements with slot attribute. It is up to application code to trigger the visibility
62+
of particular slots. `embed-page` activates slots for fetch-element handling when `src` attribute is set.
4963

50-
`slotted-element` gives ability to manipulate slots programmatically without engaging
51-
[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
64+
Using inline HTML is handy in CMS or publishing systems when content is authored by editors rather than developers.
65+
It is more performant than separate template as there is no content cloning involved.
5266

5367
## API
5468
* `slotsInit()` read slots from internal DOM to be cloned later by

demo/index.html

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
<h1> slotted-element demo </h1>
1212
<a href="https://github.com/sashafirsov/slotted-element"><img src="https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg">git</a> |
13-
<a href="render-from-json.html"> fetch-element demo </a>
13+
<a href="render-from-json.html"> fetch-element demo </a> |
14+
<a href="template-demo.html"> template demo </a>
1415
<p>
1516
<var>slotted-element</var> is web component implementing slots without shadow DOM.
1617
It is based on <var>fetch-element</var> component to fetch data and render from retrieved JSON or HTML.
@@ -26,10 +27,10 @@ <h1> slotted-element demo </h1>
2627
</slotted-element>
2728
</html-demo-element>
2829

29-
<html-demo-element>
30+
<html-demo-element title="Content would be placed inside of 'loaded' slot ">
3031
<slotted-element src="embedded.html">
3132
<fieldset>
32-
<legend> Content would be placed inside </legend>
33+
<legend> Slots located in wrapper </legend>
3334
<p slot="loading" hidden> Loading... ⏳ </p>
3435
<p slot="error" hidden> What could be wrong? </p>
3536
<p slot="loaded" hidden> Replaced with content of <b>embedded.html</b> </p>
@@ -46,13 +47,8 @@ <h1> slotted-element demo </h1>
4647
</slotted-element>
4748
</html-demo-element>
4849

49-
<html-demo-element>
50-
<slotted-element src="dwarfs.json">
51-
<fieldset>
52-
<legend> array <b>dwarfs.json</b> as table </legend>
53-
<p slot="loaded" hidden> </p>
54-
</fieldset>
55-
</slotted-element>
50+
<html-demo-element title="array dwarfs.json as table">
51+
<slotted-element src="dwarfs.json"></slotted-element>
5652
</html-demo-element>
5753

5854
<script type="module" src="https://unpkg.com/[email protected]/html-demo-element.js"></script>

demo/render-from-json.html

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8">
5-
<title>render from json - FetchElement</title>
5+
<title>render table from json - FetchElement</title>
6+
<script type="module" src="https://unpkg.com/[email protected]/html-demo-element.js"></script>
67
</head>
78
<body>
8-
<script type="module" src="https://unpkg.com/[email protected]/html-demo-element.js"></script>
9+
<h1> fetch-element render from JSON demo </h1>
10+
<a href="https://github.com/sashafirsov/slotted-element"><img src="https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg">git</a> |
11+
<a href="index.html"> slotted-element demo </a>
912

1013
<html-demo-element title="Custom renderer">
1114
<json-render-element src="doc.json"></json-render-element>
@@ -17,8 +20,8 @@
1720
render(json)
1821
{
1922
return `<h1>${ json.name }</h1>
20-
<img src="${ json.portrait }" alt="" />
21-
`;
23+
<img src="${ json.portrait }" alt="" />
24+
`;
2225
}
2326
});
2427
</script>

demo/template-demo.html

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<!doctype html>
2+
<html lang="en-GB">
3+
<head>
4+
<meta charset="utf-8">
5+
<style>
6+
7+
</style>
8+
<script type="module" src="https://unpkg.com/[email protected]/html-demo-element.js"></script>
9+
<script type="module" src="../slotted-element.js"></script>
10+
11+
</head>
12+
<body>
13+
14+
<h1> slotted-element with template demo </h1>
15+
<a href="https://github.com/sashafirsov/slotted-element"><img src="https://cdnjs.cloudflare.com/ajax/libs/octicons/8.5.0/svg/mark-github.svg">git</a> |
16+
<a href="index.html"> slotted-element demo </a>
17+
<p>
18+
<var>slotted-element</var> is web component implementing slots without shadow DOM. There are 3 ways of template use:
19+
</p>
20+
<html-demo-element title="1. Inline HTML">
21+
<slotted-element>
22+
<h6>inline HTML with slots 🎉</h6>
23+
<p slot="loading" > Not triggered as "src" attribute is not set. </p>
24+
</slotted-element>
25+
</html-demo-element>
26+
27+
<html-demo-element title="2. Template defined by ID">
28+
<template id="template-with-slots">
29+
<h6>template HTML with slots 🥳</h6>
30+
<slot name="slot0" hidden>
31+
Slots are hidden in template when "hidden" attribute is set.
32+
Could be shown by slotsAdd() method.
33+
</slot>
34+
<slot name="slot1"> slot1 is visible, hide by setting "hidden" attribute </slot>
35+
<slot name="slot3"></slot>
36+
</template>
37+
<slotted-element template="template-with-slots"></slotted-element>
38+
</html-demo-element>
39+
40+
41+
<html-demo-element title="2a. Template defined by ID, slots redefined in body.">
42+
<template id="template-with-slots2">
43+
<h6>template by ID, inline HTML redefines slots 🥳</h6>
44+
<slot name="slot0">slot0 in template would be overridden. </slot>
45+
<slot name="slot1">slot1 is defined in template. </slot>
46+
</template>
47+
<slotted-element template="template-with-slots2">
48+
<p slot="slot0">slot0 is overridden in body!</p>
49+
</slotted-element>
50+
</html-demo-element>
51+
52+
<html-demo-element title="3. template defined as property getter">
53+
<demo3-element>
54+
<p slot="slot0">slot0 is overridden in body!</p>
55+
</demo3-element>
56+
<script type="module">
57+
import SlottedElement from '../slotted-element.js';
58+
window.customElements.define('demo3-element',
59+
class Demo3Element extends SlottedElement
60+
{
61+
get template()
62+
{
63+
return `<h6>${this.nodeName}, inline HTML redefines slots 🥳</h6>
64+
<slot name="slot0">slot0 in template would be overridden. </slot>
65+
<slot name="slot1">slot1 is defined in template. </slot>
66+
`;
67+
}
68+
});
69+
</script>
70+
</html-demo-element>
71+
72+
73+
<html-demo-element title="3a. template defined as instance set property">
74+
<slotted-element id="demo3a">
75+
<p slot="slot0">slot0 is overridden in body!</p>
76+
</slotted-element>
77+
<script type="module">
78+
window.addEventListener('load', () =>
79+
{
80+
demo3a.template = `<h6>template set by external JS on instance 🥳</h6>
81+
<slot name="slot0">slot0 in template would be overridden. </slot>
82+
<slot name="slot1">slot1 is defined in template. </slot>
83+
`;
84+
demo3a.slotsInit();
85+
});
86+
</script>
87+
</html-demo-element>
88+
89+
</body>
90+
</html>

fetch-element.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,20 @@ FetchElement extends HTMLElement
6565
} ) );
6666
}
6767

68+
connectedCallback()
69+
{
70+
this.src && this.fetch( this.src );
71+
this.initialized = !0;
72+
}
73+
6874
attributeChangedCallback( name, oldValue, newValue )
6975
{
7076
switch( name )
7177
{ case 'headers':
7278
this[ name ] = eval( newValue );
7379
break;
7480
case 'src':
75-
this.fetch( newValue );
81+
this.initialized && this.fetch( newValue );
7682
break;
7783
default:
7884
if( this[ name ] !== newValue )

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "slotted-element",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"description": "Web component fetch-element for ajax and render JSON as table and slotted-element without shadow DOM",
55
"author":
66
{ "name": "Sasha Firsov",

slotted-element.js

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import FetchElement from './fetch-element.js';
2+
function createNode( tag, prop, val ){ const el = document.createElement(tag); el[prop]=val; return el; }
3+
24
export class
35
SlottedElement extends FetchElement
46
{
5-
static get template()
6-
{ return `
7-
<div slot="loading">loading...</div>
8-
<div slot="error">System error</div>
9-
<div slot="loaded"></div>
10-
` }
7+
static get observedAttributes(){ return [ 'template', ...FetchElement.observedAttributes ]; }
118

12-
constructor()
13-
{ super();
14-
this.slotsInit();
9+
connectedCallback()
10+
{ this.slotsInit();
11+
super.connectedCallback();
12+
}
13+
attributeChangedCallback( name, oldValue, newValue )
14+
{
15+
if( name !== 'template')
16+
return super.attributeChangedCallback( name, oldValue, newValue );
17+
this.template = newValue;
18+
this.initialized && this.slotsInit();
1519
}
1620

1721
fetch( url, options )
@@ -36,9 +40,31 @@ SlottedElement extends FetchElement
3640

3741
slotsInit()
3842
{
39-
this.slots = {};
40-
for( let slot of this.querySelectorAll( '[slot]' ) )
41-
this.slots[ slot.slot ] = slot;
43+
if( !this.slots )
44+
{
45+
this.slots = {};
46+
for( let node of this.querySelectorAll( '[slot]' ) )
47+
{
48+
this.slots[ node.slot ] = node;
49+
node.parentNode.replaceChild( createNode('slot', 'name', node.slot ), node );
50+
}
51+
}
52+
53+
if( this.template )
54+
{ const nodeContent = n => n && (n.content || n);
55+
let t = nodeContent(this.template.content) || nodeContent( document.getElementById( this.template ) );
56+
if( !t )
57+
t = createNode('template',"innerHTML", this.template).content;
58+
this.innerHTML='';
59+
this.appendChild( t.cloneNode(true))
60+
for( let s of this.querySelectorAll( 'slot' ) )
61+
{ let slot = this.slots[ s.name ];
62+
if( slot )
63+
{ s.hidden = !0;
64+
s.parentNode.insertBefore( slot.cloneNode( true ), s );
65+
}
66+
}
67+
}
4268
}
4369

4470
slotOnly( name )
@@ -71,10 +97,14 @@ SlottedElement extends FetchElement
7197
slotAdd( node ) // name or node created by slotClone(name)
7298
{
7399
const slot = node.slot ? node: this.slotClone( node )
74-
, ref = this.slots[ node.slot || node ];
75-
return ref && ref.parentElement.insertBefore( slot, ref );
100+
, ref = this.querySelectorAll(`slot[name="${node.slot || node}"]`);
101+
let added;
102+
for( let r of ref)
103+
added = r.parentElement.insertBefore( slot, r );
104+
return added;
76105
}
77106

78107
}
108+
export default SlottedElement;
79109

80110
window.customElements.define('slotted-element', SlottedElement);

0 commit comments

Comments
 (0)