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>
Advertisement
Answer
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>