Dynamically create a ref for items when iterating over them in lit.dev templates
When working with lit.dev templates, very occasionally I find myself needing a reference to DOM elements that are being created inside a loop. Typically, this will be when I need to run animations on them and need a reference to them to do so.
Suppose our render function looks like this:
render() {
return html`
${this.speakers.map(
(speaker: any) => html`
<div class="wht-Title">
<p class="wht-Speaker">${speaker.name}</p>
<p class="wht-Context">${speaker.context}</p>
</div>
`
)}
`;
}
And I want to have a reference to each of the wht-Title
elements. There is no built-in way I know of to create a reference dynamically. However, this Stack Overflow answer for React based project was close enough that adapting it for lit.dev was a breeze.
As we loop over the items we can push them into an array that we later access. So we need our array, and a function to push the refs into it:
@property()
titleRefs: any = [];
setRef = (ref: any) => {
// lit sometimes pushes in an undefined so return early if that happens
if (ref === undefined) {
return;
}
this.titleRefs.push(ref);
};
Yea, yea, my types are any
, I don’t give two shits TypeScript, I’m just getting this thing working, capiche?
And then we adapt our loop to use that function to push a reference in:
render() {
return html`
${this.speakers.map(
(speaker: any) => html`
<div ${ref(this.setRef)} class="wht-Title">
<p class="wht-Speaker">${speaker.name}</p>
<p class="wht-Context">${speaker.context}</p>
</div>
`
)}
`;
}
And now, by firstUpdated()
we have those elements stored in our this.titleRefs
property.
And we can access them like this using the index (idx
) where needed to match up the appropriate element:
this.titleRefs.map((title: any, idx: number) => {
title.animate(this.showTitle, {
delay: this.speakers[idx].timestamp * 1000,
duration: 300,
fill: "forwards",
easing: "ease-in-out",
});
});
Leave a Reply