ajaxとかで変化するテキストをtweetボタンでつぶやきたい。

More than 1 year has passed since last update.

まえおき

http://kakiage.org/html/whosdoingwhat.html

こういうお遊びゲームを作ったんです。
「だれが」「どこで」「どうする」を入力して投稿します。投稿された内容はサーバに記録されて、「文章カモン!」ボタンを押すと、サーバに登録されたそれぞれの単語がランダムに選択されて表示されるっていうやつ。

bouzuganoharadesabawomusu.png

ひだまりスケッチにあった「坊主が 野原で サバを 蒸す!」っていうあれです。
ネタに困った小説家がインスピレーションを掻き立てるツールになればいいなーという感じで。

このプログラム自体は簡単なんでちゃっちゃとできるんで特に解説はしません。
サーバとのデータのやりとりはAjaxでやってます。簡単でいいですよね、jQuery。

ツイートしたいよね。でもうまくいかない罠

さて、何かの奇跡で面白い文章が生成されたらtwitterに投稿したいですよね。
twitterさんはtweetボタン生成サービスを提供してくれています。
https://about.twitter.com/resources/buttons
これで適当にコード生成します。こんなやつ。

<a href="https://twitter.com/share" class="twitter-share-button" data-text="あああああ" data-via="KakiageNovel" data-lang="ja" data-related="KakiageNovel" data-hashtags="かきあげ!">ツイート</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>

data-textのところをつぶやきたい内容にかえればおっけー。Ajaxで受信した内容で置き換えればいいじゃん。ひゃっほー。

と思うじゃないですかー。うまくいかないんですよキーッ( ゚皿゚)!!

即時実行関数がなんかやってる

で、よくよくコードミてみるとJavaScriptのところ、!function(d,s,id){...}(...)ってなってるんですね。即時実行関数ですね。ロードされた瞬間、この関数が実行されます。ここでツイートする内容が決定しちゃってるんですね。
htmlがロードされた瞬間はまだ、つぶやきたい内容とか決まってないですしお寿司。

ということはロードされた瞬間に実行させなきゃいいんです。頭の!マークをとっちゃいましょう。

簡単のためにここでは、ボタンを押したら適当な文字列が生成されて、その生成された文字列を表示するプログラムを例にします。

<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>test</title>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
    <div id="test_text"></div>
    <div id="twbtn"></div>
    <button type="button" onclick="insertText();">なんかだす</button>
<script>
    var arr = ["いち", "に", "さん", "よん", "ごー", "ろく", "なな", "はち", "きゅう", "じゅう"];
    var insertText = function(){
        var text = arr[Math.floor(Math.random()*10)];
        $('#test_text').text(text);
        var button = '<a href="https://twitter.com/share" class="twitter-share-button" data-text="' + text +'" data-via="KakiageNovel" data-lang="ja" data-related="KakiageNovel" data-hashtags="かきあげ!">ツイート</a>';
        $('#twbtn').html(button);
        prepareTweet(document, 'script', 'twitter-wjs');
    };

    var prepareTweet = function(d,s,id){
        var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
        if(!d.getElementById(id)){
            js=d.createElement(s);
            js.id=id;
            js.src=p+'://platform.twitter.com/widgets.js';
            fjs.parentNode.insertBefore(js,fjs);
        }
    };
</script>
</body>
</html>

ボタンを押したら、arr という配列の中のランダムな要素を表示して、それをつぶやくtweetボタンを表示する(つもり)のプログラムです。
twitterさんが吐き出したJavaScriptはprepareTweetという関数にしました。
ボタンを押したら、twitterさんが吐き出したaタグみたいなのを作って、prepareTweetするーというものです。

とりあえず、最初の一回はうまくいきました。tweetボタンもでました! 内容も大丈夫!

でも、もう一回ボタンを押すと、あれれ、内容が変化しない……。

原因は……

仕方ないので、twitterさんが吐き出したJavaScriptをよく読んでみます。
すると twitter-wjs という idがついてるタグがない場合はそういうidのタグを追加する処理のようです。
ここでは http://platform.twitter.com/widgets.js というJavaScriptを呼び出す scriptタグが追加されます。

中身をちらっと確認すると、 貴様も即時実行関数か!!

つまり、twitterさんのtweetボタン表示スクリプトが何をやっているかというと

  • widgets.js というJavaScriptを読み込む
  • widgets.js さんはtwitterさんが吐き出した aタグの内容を確認して、aタグをtweetボタンで置き換える処理をしている

のでした。widgets.jsの中身を書き換えることはできないのでこまっちゃいますね。

解決

即時実行しやがるのであれば、もう一回即時実行させればいいだけの話です。
twitter-wjsというidが挿入されるということを突き止めているので、このIDのタグを削除して、もう一回作らせればいいだけです。

つまり、コードはこんな感じ

<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>test</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
    <div id="test_text"></div>
    <div id="twbtn"></div>
    <button type="button" onclick="insertText();">なんかだす</button>
<script>
    var arr = ["いち", "に", "さん", "よん", "ごー", "ろく", "なな", "はち", "きゅう", "じゅう"];
    var insertText = function(){
        var text = arr[Math.floor(Math.random()*10)];
        $('#test_text').text(text);
        var button = '<a href="https://twitter.com/share" class="twitter-share-button" data-text="' + text +'" data-via="KakiageNovel" data-lang="ja" data-related="KakiageNovel" data-hashtags="かきあげ!">ツイート</a>';
        $('#twbtn').html(button);
        if(twjs = $('#twitter-wjs')) {
            twjs.remove();
        }
        prepareTweet(document, 'script', 'twitter-wjs');
    };

    var prepareTweet = function(d,s,id){
        var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
        if(!d.getElementById(id)){
            js=d.createElement(s);
            js.id=id;
            js.src=p+'://platform.twitter.com/widgets.js';
            fjs.parentNode.insertBefore(js,fjs);
        }
    };
</script>
</body>
</html>

これで、つぶやきたい内容をつぶやけるtweetボタンができました。

めでたし、めでたし。

しかし、どうして、生成される文章の結果、「五右衛門風呂に入る」が多い……。

追記 もっと簡単に

いや、この方法だとボタン作るたびにwidgets.jsをとりにいくので、負荷的にあれだなー、僕ならそんな不審なアクセス遮断しちゃうなーとか思ってました。

どうやら、twttr.widgets.load()というメソッドつかうと、再読み込みしてくれるそうです。

nandatte--.jpg

あれ、ちょっとまて。最初にtwitterさんが吐き出した即時実行のJavaScriptのコード、<script id="twitter-wjs" src="http://platform.twitter.com/widgets.js"></script> というタグを最初に出てきたscriptタグのところに埋め込むだけのコードじゃないですか。それならいっそのこと、最初っからscriptタグ書いておいたほうがよくない?

というわけで改良版です。

<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>test</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
    <script id="twitter-wjs" src="http://platform.twitter.com/widgets.js"></script>
</head>
<body>
    <div id="test_text"></div>
    <div id="twbtn"></div>
    <button type="button" onclick="insertText();">なんかだす</button>
    <script>
        var arr = ["いち", "に", "さん", "よん", "ごー", "ろく", "なな", "はち", "きゅう", "じゅう"];
        var insertText = function(){
            var text = arr[Math.floor(Math.random()*10)];
            $('#test_text').text(text);
            var button = '<a href="https://twitter.com/share" class="twitter-share-button" data-text="' + text +'" data-via="KakiageNovel" data-lang="ja" data-related="KakiageNovel" data-hashtags="かきあげ!">ツイート</a>';
            $('#twbtn').html(button);
            twttr.widgets.load();
        };
    </script>
</body>
</html>

コードもすっきりしましたし、twitterさんにも迷惑かけない感じします。

これでほんとうにめでたしめでたし。

コメントにて @kimama1997 さんにおしえてもらいました。ありがとうございます!