I want to create a Web Component. I use a ShadowDom and after generating a list I want to add a click Event to each element of the list. But I wonder how to access the list.
Neither the document nor the template shows me the items after a querySelect.
My question: How can I access the generated list within the webcomponent?
const news =
{
uk: [{
id: 1,
title: "News 1 UK",
body: "lorem ipsum"
},
{
id: 2,
title: "News 2 UK",
body: "lorem ipsum"
},
{
id: 3,
title: "News 3 UK",
body: "lorem ipsum"
},
{
id: 4,
title: "News 4 UK",
body: "lorem ipsum"
},
],
de: [
{
id: 1,
title: "News 1 DE",
body: "lorem ipsum"
},
{
id: 2,
title: "News 2 DE",
body: "lorem ipsum"
},
{
id: 3,
title: "News 3 DE",
body: "lorem ipsum"
},
{
id: 4,
title: "News 4 DE",
body: "lorem ipsum"
},
]
};
class MyNews {
#limit = 10;
#region = 'uk';
news = [];
constructor(conf = {}) {
this.#limit = conf.limit ?? this.#limit;
this.#region = conf.region ?? this.#region;
this.news = news[this.#region]
}
showNews() {
const items = this.news.slice(0,this.#limit);
return items.map((n,i) => {
return `<p>${i+1}. ${n.title}</p>`;
}).join('');
}
getNewsData() {
return this.news;
}
}
class NewsTicker extends HTMLElement {
constructor() {
super();
this.name = 'World News';
this.limit = 10;
this.region = "uk"
}
static get observedAttributes() {
return ['name', 'url', 'limit', 'region'];
}
attributeChangedCallback(property, oldValue, newValue) {
if (oldValue === newValue) return;
this[ property ] = newValue;
}
async connectedCallback() {
const options = {
url: this.url,
limit: this.limit,
region: this.region
};
const myNews = new MyNews(options);
const
shadow = this.attachShadow({ mode: 'closed' }),
template = document.getElementById('news-template').content.cloneNode(1),
contextTitle = `Context ${ this.name } !`;
template.querySelector('.news-context-title').textContent = contextTitle;
template.querySelector('.news-list').innerHTML = myNews.showNews();
shadow.append( template );
const list = document.querySelector('.news-list');
console.log("try to get list inner the template tag:", list)
}
}
customElements.define( 'news-ticker', NewsTicker );
<news-ticker
name="News DE"
region="de"
limit="2">
</news-ticker>
<template id="news-template">
<style>
h2 {
text-align: center;
font-weight: normal;
padding: 0.5em;
margin: 1px 0;
background-color: black;
color: white;
border: 1px solid #666;
font-weight: bold;
}
.news-list > p {
font-weight: normal;
border: 1px solid #787878;
padding: 0.3em;
border-radius: 5px;
margin: 0.2em;
text-transform: capitalize;
text-align: left;
}
.news-list p:hover {
cursor: pointer;
background-color: #ffffd0;
}
</style>
<h2 class="news-context-title"></h2>
<div class="news-list"></div>
</template>
<h1></h1>
You dont have access to the template over document. The template tag is a shadow DOM. You can make avaible for access the shadow DOM if you change the mode parameter to true: this.attachShadow({ mode: 'open' })
. Then you can use this.shadowRoot.querySel...
. Otherwise you can access directly over your shadow object (shadow = this.attachShadow({ mode: 'closed' })
) with shadow.querySelector()
.
const news =
{
uk: [{
id: 1,
title: "News 1 UK",
body: "lorem ipsum"
},
{
id: 2,
title: "News 2 UK",
body: "lorem ipsum"
},
{
id: 3,
title: "News 3 UK",
body: "lorem ipsum"
},
{
id: 4,
title: "News 4 UK",
body: "lorem ipsum"
},
],
de: [
{
id: 1,
title: "News 1 DE",
body: "lorem ipsum"
},
{
id: 2,
title: "News 2 DE",
body: "lorem ipsum"
},
{
id: 3,
title: "News 3 DE",
body: "lorem ipsum"
},
{
id: 4,
title: "News 4 DE",
body: "lorem ipsum"
},
]
};
class MyNews {
#limit = 10;
#region = 'uk';
news = [];
constructor(conf = {}) {
this.#limit = conf.limit ?? this.#limit;
this.#region = conf.region ?? this.#region;
this.news = news[this.#region]
}
showNews() {
const items = this.news.slice(0,this.#limit);
return items.map((n,i) => {
return `<p>${i+1}. ${n.title}</p>`;
}).join('');
}
getNewsData() {
return this.news;
}
}
class NewsTicker extends HTMLElement {
constructor() {
super();
this.name = 'World News';
this.limit = 10;
this.region = "uk"
}
static get observedAttributes() {
return ['name', 'url', 'limit', 'region'];
}
attributeChangedCallback(property, oldValue, newValue) {
if (oldValue === newValue) return;
this[ property ] = newValue;
}
async connectedCallback() {
const options = {
url: this.url,
limit: this.limit,
region: this.region
};
const myNews = new MyNews(options);
const
shadow = this.attachShadow({ mode: 'open' }), // change mode to open then you have access over the shadowRoot
template = document.getElementById('news-template').content.cloneNode(1),
contextTitle = `Context ${ this.name } !`;
template.querySelector('.news-context-title').textContent = contextTitle;
template.querySelector('.news-list').innerHTML = myNews.showNews();
shadow.append( template );
const list = document.querySelector('.news-list');
const list_1 = shadow.querySelector('.news-list');
const list_2 = this.shadowRoot.querySelector('.news-list');
console.log("document.querySelector('.news-list') :", list_1)
console.log("shadow.querySelector('.news-list') :", list_2);
console.log("this.shadowRoot.querySelector('.news-list') :", list_3);
}
}
customElements.define( 'news-ticker', NewsTicker );
<news-ticker
name="News DE"
region="de"
limit="2">
</news-ticker>
<template id="news-template">
<style>
h2 {
text-align: center;
font-weight: normal;
padding: 0.5em;
margin: 1px 0;
background-color: black;
color: white;
border: 1px solid #666;
font-weight: bold;
}
.news-list > p {
font-weight: normal;
border: 1px solid #787878;
padding: 0.3em;
border-radius: 5px;
margin: 0.2em;
text-transform: capitalize;
text-align: left;
}
.news-list p:hover {
cursor: pointer;
background-color: #ffffd0;
}
</style>
<h2 class="news-context-title"></h2>
<div class="news-list"></div>
</template>