ARTICLES

Speakerdeck の Embed をコンポーネントにして、MDX に埋め込む

2019-04-26

Speakerdeck の埋め込み表示が、SlideShare よりもいい感じなので、移行しようと思って MDX 向けにコンポーネントを作ってみたところ、ちょっとエラーが起きているのですが、利用は出来ているので途中経過報告。

まず、Next.js で Speakerdeck の通常のエンベッドコードを HTML 出力時に直接埋め込むようにしました。
フッター以下で、スクリプトを読み込み、

<script async src="//speakerdeck.com/assets/embed.js" />

適当な場所で class 名などを指定します。エンベッド用のコードをみてみると、speakerdeck-embed クラスを探してマウントするようでした。なので、別途 div を指定することも出来ます。

<div className="speakerdeck-embed" data-id={this.props.id} data-ratio="1.77777777777778" />

これは、うまくいきましたが、エラーが出ました。

エラーは以下のようなものです。

Uncaught DOMException: Blocked a frame with origin "https://speakerdeck.com" from accessing a cross-origin frame. at e.value (https://speakerd.herokuapp.com/assets/player-c798dabe4938945bc885.js:1:4710) at new e (https://speakerd.herokuapp.com/assets/player-c798dabe4938945bc885.js:1:3565) at Function.c.init (https://speakerd.herokuapp.com/assets/player-c798dabe4938945bc885.js:1:12891) at HTMLDocument.< anonymous > (https://speakerd.herokuapp.com/assets/player-c798dabe4938945bc885.js:1:2400)

他にも、Speakerdeck の埋め込みを可能にする別のサービスを利用してみましたが、どうやらこのサイト上でも同様のエラーが出ていました。

Speakerdeck は、oEmbed という共通のエンベッド方式の API に対応しており、以下のような形でスライドの URL を渡すと、JSON で埋め込み関係の情報を返してくれます。

https://speakerdeck.com/oembed.json?url=https://speakerdeck.com/karad/mdx-with-next-dot-js

以下のような感じです。

{
    "type": "rich",
    "version": 1,
    "provider_name": "Speaker Deck",
    "provider_url": "https://speakerdeck.com/",
    "title": "MDX with Next.js",
    "author_name": "kazuhiro hara",
    "author_url": "https://speakerdeck.com/karad",
    "html": "<iframe id=\"talk_frame_512582\" src=\"//speakerdeck.com/player/b5eb02fb5ab24c729fe8c9bd5aa7d908\" width=\"710\" height=\"399\" style=\"border:0; padding:0; margin:0; background:transparent;\" frameborder=\"0\" allowtransparency=\"true\" allowfullscreen=\"allowfullscreen\" mozallowfullscreen=\"true\" webkitallowfullscreen=\"true\"></iframe>\n",
    "width": 710,
    "height": 399
}

エンベッド ID を返してくれるともう少し楽なのですが、そうもいかないので、この HTML を JSX にして利用してみます。

以下のような形でエンベッド ID を指定する共通のコンポーネントとしました。多少のカスタマイズをしています。

import React from 'react'

interface SpeakerDeckProps {
    id: string
}

export class SpeakerDeck extends React.Component<SpeakerDeckProps, {}> {
    constructor(props: SpeakerDeckProps) {
        super(props);
    }

    render() {
        return this.props.id ?
            (
                <div style={{left: 0, width: '100%', height: 0, position: 'relative', paddingBottom: '56.1987%', marginBottom: '2rem'}}>
                    <iframe src={`//speakerdeck.com/player/${this.props.id}`} style={{border: 0, top: 0, left: 0, width: '100%', height: '100%', position: 'absolute'}} frameBorder="0" allowFullScreen></iframe>
                </div>
            ) : (
                false
            )
    }
}

これで、以下のような呼び出しが MDX 内で可能となりました。

<SpeakerDeck id="b5eb02fb5ab24c729fe8c9bd5aa7d908"/>

ただし、依然として前述のエラーが出ます。このエンベッドでは、どうも iframe 内から親要素にアクセスするタイプのものなようで、そのためエラーが起きているようでした。ざっと調べたところ、iframe 内のコンテンツの高さに応じて親要素のサイズを変更しているのではないかと見受けられるふしがあり、これが原因なのではと推測しています。

この件、引き続き調査を続けてみます。


ARTICLES


    AUTHOR

    原 一浩 の顔写真

    (はら) 一浩(かずひろ)

    カンソクインダストリーズ代表 / グレーティブ合同会社代表

    1998年に独立し、同年、ウェブデザイン専門のメールメディア DesignWedgeの発行を開始。Webデザイン業の傍ら、海外のWebデザインに関する情報発信を行う。
    雑誌への寄稿多数。主な著書に『はじめてのフロントエンド開発』『プロセスオブウェブデザイン』、『Play framework徹底入門』、『ウェブデザインコーディネートカタログ』など。自社製のWebデザインのクロール&キャプチャシステムvaqumをベースに、様々なリサーチを行っている。Web 検定プロジェクトメンバー

    著作一覧はこちら