DOM

【JavaScript】 吹き出しをcssを使用しないで動的に作成する

更新日:2021/03/04

僕がWebツールを作成しているとき、吹き出しを動的に作成する必要がありました。

しかし、これが意外と難しい。

beforeやafterなどの疑似要素を使用しているのがネックになっているようなので、対処方法を考えてみました。

 

吹き出しをJavaScriptのみで作るのは難しい

通常cssを使用して吹き出し表示する場合、以下の例のようにbeforeやafterなどの疑似要素を使用します。

html


<div class="fukidasi"></div>

css


.fukidasi{
  border: 1px solid blue;
  width: 150px;
  height: 50px;
  border-radius: 10px;
  position: relative;
  background: white;
}
.fukidasi:after,.fukidasi:before{
  content: "";
  position: absolute;
  width: 0;
  height: 0;
}
.fukidasi:after{
  border-top: 4px solid transparent;
  border-left:  15px solid blue;
  border-bottom:  7px solid transparent;
  top:10px;
  right: -15px;
  z-index:0;
}
.fukidasi:before{
  border-top: 3px solid transparent;
  border-left:  14px solid white;
  border-bottom:  6px solid transparent;
  top:11px;
  right: -14px;
  z-index: 1;
}

これをcssを使用しないでJavaScriptのみでスタイル属性を設定してみます。

div要素については、次のようにstyleプロパティを一つ一つ設定していきます。


const div = document.getElementsByClassName("fukidasi")[0];
div.style.border = "1px solid blue";
div.style.width = "150px";
// 以下略

では、beforeやafterなどの疑似要素のstyleプロパティはどうやってアクセスすればいいのかというと…

DOMは、疑似要素を操作できません。
なので、アクセスできません。

 

JavaScriptで動的に疑似要素を作成

JavaScriptで動的に疑似要素を作成するには、今のところstyleタグをhtml内に挿入するしかありません。

スマートな方法ではありませんが、仕方がないのでやってみます。


window.addEventListener( "DOMContentLoaded" , ()=> {

        // div要素にスタイル属性をセット
    const divStyle ={
        border: "1px solid blue",
        width: "150px",
        height: "50px",
        "border-radius": "10px",
        position: "relative",
        background: "white",
    };
    const div = document.getElementsByClassName("fukidasi")[0];
    Object.entries( divStyle ).forEach( e => div.style[ e[0] ]=e[1]);

        // ランダムなクラス名を作成し、divに追加
    const addClass = "fukidasi-"
        + Array.from( {length:8},
                ()=>String.fromCharCode("a".charCodeAt(0) + Math.floor(Math.random() * 26) )
            ).join("");

    div.classList.add(addClass);

        // 疑似要素のスタイルシート作成
    const pseudoStyle =
        `.fukidasi.${addClass}:after,.fukidasi.${addClass}:before{
                content: "";
                position: absolute;
                width: 0;
                height: 0;
        }
        .fukidasi.${addClass}:after{
                border-top: 4px solid transparent;
                border-left:  15px solid blue;
                border-bottom:  7px solid transparent;
                top:10px;
                right: -15px;
                z-index:0;
        }
        .fukidasi.${addClass}:before{
                border-top: 3px solid transparent;
                border-left:  14px solid white;
                border-bottom:  6px solid transparent;
                top:11px;
                right: -14px;
                z-index: 1;
        }`;

    const pseudoElement = document.createElement("style");
    pseudoElement.innerHTML = pseudoStyle;

        // スタイルシートをhtmlのhead内に設置
    document.getElementsByTagName("head")[0].appendChild(pseudoElement);

});

まずはdiv要素のstyleプロパティにスタイルを設定します。

次にランダムなクラス名を作成しています。
これは、これから追加する疑似要素のスタイルを、他のfukidasiクラスを持つ要素に影響させないためです。
ちなみに疑似要素は英語で、pseudoと書くようです。

次に疑似要素のスタイルとしてセットする文字列を作成します。
ここではテンプレートリテラルを使用しているので、字下げしたスペースやタグも文字列内に取り込まれます。無駄に感じるなら、字下げ分は削除するのもいいかもしれません。

次にstyle要素を作成し、head要素内に追加して終了です。
追加先はbodyでも問題ありません。
お好きなほうでどうぞ。

 

疑似要素にこだわる必要はない

吹き出しは疑似要素を使用するのが主流のようですが、そもそも疑似要素にこだわる必要はありません。

divを3つ重ねる方法でも表示できます。

html


<div class="fukidasi2">
    <div class="frame"></div>
    <div class="triangle1"></div>
    <div class="triangle2"></div>
</div>

css


.fukidasi2{
    width: 165px;
    height: 50px;
    position: relative;
}
.fukidasi2 div.frame{
    padding: 0;
    width: 150px;
    height: 50px;
    margin: 0;
    border: 1px solid blue;
    border-radius: 10px;
    background: white;
    z-index: 0;
    position: absolute;
    top: 0;
    left: 0;
    box-sizing: border-box;
}
.fukidasi2 div.triangle1{
    position: absolute;
    width: 0;
    height: 0;
    border-top: 4px solid transparent;
    border-left: 15px solid blue;
    border-bottom: 7px solid transparent;
    top: 10px;
    right: 0;
    z-index: 1;
}
.fukidasi2 div.triangle2{
    position: absolute;
    width: 0;
    height: 0;
    border-top: 3px solid transparent;
    border-left: 15px solid white;
    border-bottom: 6px solid transparent;
    top: 11px;
    right: 1px;
    z-index: 2;
}

これなら、JavaScriptで動的に作成できます。

styleタグに追加すると処理に時間がかかりそうだからイヤというひとは、こちらを検討してみてください。

更新日:2021/03/04

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。