Published: 6/12/2019
I use CodePens in my blog posts and recently I noticed that there is an issue which prevents displaying CodePen embeds correctly. I use Contentful to manage blog posts content. Contentful supports embedding content via Embedly.
But for some reason, Embedly fails to show my CodePens in some cases. I couldn't find the reason nor any way to load them. They just didn't load. So I decided to stop hitting my head against the wall and make an implementation without Embedly.
I still use the existing embed feature in Contentful to embed CodePens. This way I get a nice preview on Contentful editor. But on the client-side, I load CodePens differently. Also because I don’t load Embedly’s Platform.js anymore I get a minor performance boost by loading fewer scripts.
Contentful embedding produces HTML like below. We will turn this into a CodePen embed.
<p>
<a href="https://codepen.io/teroauralinna/pen/rZZGpe" class="embedly-card">
Embedded content: https://codepen.io/teroauralinna/pen/rZZGpe
</a>
</p>
This new service will handle the CodePen embed script loading on-demand and then it will load embedded pens.
Method initCodePens
finds all the .embedly-card
elements and inserts needed attributes.
After that loadCodePens
will either insert CodePen embed script into the head or if the script is already loaded it will call the method to load CodePens.
import { Injectable, Inject, PLATFORM_ID } from '@angular/core'
import { isPlatformBrowser } from '@angular/common'
import { environment } from 'environments/environment'
@Injectable()
export class CodePenEmbedService {
private CODEPEN_REGEX: RegExp = new RegExp(`https://codepen.io/${environment.codePenSettings.user}/pen/([a-zA-Z0-9]+)`)
private CODEPEN_SCRIPT_ID: string = 'codepen-script'
private CODEPEN_SCRIPT_SRC: string = 'https://static.codepen.io/assets/embed/ei.js'
constructor(@Inject(PLATFORM_ID) private platformId: Object) {
}
public init() {
if (isPlatformBrowser(this.platformId)) {
this.initCodePens()
this.loadCodePens()
}
}
private initCodePens() {
const embeds = Array.from(document.querySelectorAll('.embedly-card'))
embeds.forEach(embed => {
const codePenUrl = embed.getAttribute('href')
const match = codePenUrl.match(this.CODEPEN_REGEX)
const codePenId = match ? match[1] : null
if (codePenId) {
const embedParent = embed.parentElement
embedParent.classList.add('codepen')
embedParent.setAttribute('data-height', '700')
embedParent.setAttribute('data-theme-id', 'dark')
embedParent.setAttribute('data-default-tab', 'result')
embedParent.setAttribute('data-user', environment.codePenSettings.user)
embedParent.setAttribute('data-slug-hash', codePenId)
embedParent.setAttribute('data-preview', 'true')
}
})
}
private loadCodePens() {
if (document.getElementById(this.CODEPEN_SCRIPT_ID)) {
window.__CPEmbed('.codepen')
} else {
const script = document.createElement('script')
script.id = this.CODEPEN_SCRIPT_ID
script.src = this.CODEPEN_SCRIPT_SRC
script.type = 'text/javascript'
script.defer = true
document.querySelector('head').appendChild(script)
}
}
}
import { CodePenEmbedService } from '@services/code-pen-embed.service'
@NgModule({
...
providers: [
...
CodePenEmbedService
],
...
})
Lifecycle method ngAfterViewChecked
will be called multiple times so codePenNeedsInitialize
variable must be set to true
when CodePens need initialization.
import { CodePenEmbedService } from '@services/code-pen-embed.service'
export class ExampleComponent implements AfterViewChecked {
private codePenNeedsInitialize: boolean = true
constructor(
private codePenEmbedService: CodePenEmbedService
) {}
ngAfterViewChecked() {
if (this.codePenNeedsInitialize) {
this.codePenEmbedService.init()
this.codePenNeedsInitialize = false
}
}
}
Be the first commenter?
I am an experienced web developer with an eye for solid UI/UX design. I have specialized in front-end development, responsive web design, design systems, modern web frameworks, and content management systems. I also have experience in mobile apps development and back-end coding with PHP, Node.js, and Java. So I have a full stackish background, but I'm enjoying most building robust and beautiful front-ends with performance, accessibility, and testability in mind.
© Tero Auralinna
Auralinna.fiSunset with Bubbles: Travel and Photography Blog