I've been needing a bit of a pallet cleanse from a work perspective so I dug into our issue queue and found what I figured would be easy: Embedding twitter conversations #370
Then as I started into it I was like "hmm.. this might make a useful tutorial" so I asked what people wanted it in:
Here's the full video walk through, below I'll post code and some things covered
Links in the video
- NPM: https://www.npmjs.com/package/@lrnwebcomponents/twitter-embed
- Source: https://github.com/elmsln/lrnwebcomponents/tree/master/elements/twitter-embed
- Iframe sandbox: https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
- Iframe Lazy load: https://web.dev/iframe-lazy-loading/
VanillaJS
Here's the code for the Vanilla version, it's 100ish lines
class TwitterEmbed extends HTMLElement { static get tag() { return "twitter-embed"; } /** * HTMLElement spec / class based architecture in general */ constructor() { super(); this.dataWidth = this.getAttribute("data-width") ? this.getAttribute("data-width") : "550px"; this.dataTheme = this.getAttribute("data-theme") ? this.getAttribute("data-theme") : "light"; this.tweet = this.getAttribute("tweet") ? this.getAttribute("tweet") : null; this.tweetId = this.getAttribute("tweet-id") ? this.getAttribute("tweet-id") : null; this.allowPopups = this.getAttribute("no-popups") ? "" : "allow-popups"; } /** * HTMLElement spec */ static get observedAttributes() { return ["tweet", "data-width", "data-theme", "tweet-id", "no-popups"]; } /** * HTMLElement spec */ attributeChangedCallback(attr, oldValue, newValue) { if (attr == "tweet" && newValue && newValue.includes("twitter.com")) { this.tweetId = newValue.split("/").pop(); } if (attr == "no-popups") { this.allowPopups = newValue == "no-popups" || newValue == "" || !newValue || newValue == null || newValue == "null" ? "" : "allow-popups"; } if (["no-popups", "tweet-id", "data-width", "data-theme"].includes(attr)) { this.innerHTML = this.html; } } get dataWidth() { return this.getAttribute("data-width"); } set dataWidth(value) { if (value == null || !value) { this.removeAttribute("data-width"); } else { this.setAttribute("data-width", value); } } get dataTheme() { return this.getAttribute("data-theme"); } set dataTheme(value) { if (!value || !["dark", "light"].includes(value)) { this.dataTheme = "light"; } else { this.setAttribute("data-theme", value); } } get tweetId() { return this.getAttribute("tweet-id"); } set tweetId(value) { if (value == null) { this.removeAttribute("tweet-id"); } else { this.setAttribute("tweet-id", value); } } get tweet() { return this.getAttribute("tweet"); } set tweet(value) { if (value == null) { this.removeAttribute("tweet"); } else { this.setAttribute("tweet", value); } } /** * my own convention, easy to remember */ get html() { return ` <div class="twitter-tweet twitter-tweet-rendered" style="display: flex; max-width: ${ this.dataWidth }; width: 100%; margin-top: 10px; margin-bottom: 10px;"> <iframe sandbox="allow-same-origin allow-scripts ${this.allowPopups}" scrolling="no" frameborder="0" loading="lazy" allowtransparency="true" allowfullscreen="true" style="position: static; visibility: visible; width: ${ this.dataWidth }; height: 498px; display: block; flex-grow: 1;" title="Twitter Tweet" src="https://platform.twitter.com/embed/index.html?dnt=true&&frame=false&hideCard=false&hideThread=false&id=${ this.tweetId }&lang=en&origin=http%3A%2F%2Flocalhost%3A8000%2Felements%2Ftwitter-embed%2Fdemo%2Findex.html&theme=${ this.dataTheme }&widgetsVersion=223fc1c4%3A1596143124634&width=${this.dataWidth}" data-tweet-id="${this.tweetId}"> </iframe> </div>`; } } customElements.define(TwitterEmbed.tag, TwitterEmbed); export { TwitterEmbed }; Pros
- 0 dependencies
- platform level code only, all valid conventions
- should theoretically work forever and in any platform
Cons
- verbose to get data binding to be what's expected
- some weird constructor things / default value stuff
- re-renders are heavy handed
LitElement
LitElement is a popular
import { LitElement, html } from "lit-element/lit-element.js"; class TwitterEmbedLit extends LitElement { static get tag() { return "twitter-embed-lit"; } /** * HTMLElement spec */ constructor() { super(); this.dataWidth = "550px"; this.dataTheme = "light"; this.tweet = null; this.tweetId = null; this.allowPopups = "allow-popups"; } /** * LitElement properties definition */ static get properties() { return { tweet: { type: String }, dataWidth: { type: String, attribute: "data-width" }, dataTheme: { type: String, attribute: "data-theme" }, tweetId: { type: String, attribute: "tweet-id" }, noPopups: { type: Boolean, attribute: "no-popups" }, allowPopups: { type: String } }; } /** * LitElement equivalent of attributeChangedCallback */ updated(changedProperties) { changedProperties.forEach((oldValue, propName) => { if (propName === "noPopups") { if (this[propName]) { this.allowPopups = ""; } else { this.allowPopups = "allow-popups"; } } if ( propName === "tweet" && this[propName] && this[propName].includes("twitter.com") ) { this.tweetId = this[propName].split("/").pop(); } }); } /** * Popular convention / LitElement */ render() { return html` <div class="twitter-tweet twitter-tweet-rendered" style="display: flex; max-width: ${this .dataWidth}; width: 100%; margin-top: 10px; margin-bottom: 10px;" > <iframe sandbox="allow-same-origin allow-scripts ${this.allowPopups}" scrolling="no" frameborder="0" loading="lazy" allowtransparency="true" allowfullscreen style="position: static; visibility: visible; width: ${this .dataWidth}; height: 498px; display: block; flex-grow: 1;" title="Twitter Tweet" src="https://platform.twitter.com/embed/index.html?dnt=true&frame=false&hideCard=false&hideThread=false&id=${this .tweetId}&lang=en&origin=http%3A%2F%2Flocalhost%3A8000%2Felements%2Ftwitter-embed%2Fdemo%2Findex.html&theme=${this .dataTheme}&widgetsVersion=223fc1c4%3A1596143124634&width=${this .dataWidth}" data-tweet-id="${this.tweetId}" > </iframe> </div> `; } } customElements.define(TwitterEmbedLit.tag, TwitterEmbedLit); export { TwitterEmbedLit }; Cons
- 1 dependency
- Some library specific conventions
Pros
- conventions are simple
- lit-html template rewriting conventions may become platform level code in the future
- tidy code, slightly less to write, easier to read
- faster re-render, though very small to see in this example
Top comments (0)