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)