MENU

JavaScriptDOM

【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タグの追加すると処理に時間がかかりそうだからイヤというひとは、こちらを検討してみてください。

けーちゃんおススメJavaScript入門書

  • スラスラ読める JavaScript ふりがなプログラミング
  • プログラム未経験者がJavaScript始めるならコレ!
    コードを掲載して自分で理解しろという投げっぱなしな入門書とは異なり、コードに一つ一つどんなことをやっているかをふりがなという形式で解説しています。
    それでいてJavaScriptの基礎と応用を学べる良書です。
  • これからWebをはじめる人のHTML&CSS、JavaScriptのきほんのきほん
  • JavaScriptの機能を実践で活かすにはHTMLやCSSの知識が不可欠です。
    しかしそれらの知識があることが前提として書かれている書籍が多い中、この本は総合的な知識を身に着けることができます。
    HTMLやCSSの知識も不安な方には、ぴったりの一冊です
  •  

    入門書の役割は、自分のやりたいことをネットで調べることができるようになるための、基礎的な知識の獲得です。
    まずはこれらの本でしっかりと基礎知識を身につけましょう。
    そしてもっと高度なことや専門的なことはネットで調べ、情報が足りないと感じたら書籍を購入してください。


    期間限定情報:
    7/16から7/18は63時間のビッグセール!
    欲しかったアレが安く手に入るチャンスです
    忘れずにチェックしてください!
    僕は以前のタイムセール祭りで4Kモニタが買ったけど、それより安かったらどうしよう・・・

    さらにお得なポイントアップキャンペーンも同時開催!

    記事の内容について

     

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


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

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

    そんなときは、ご意見もらえたら嬉しいです。

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

    【お願い】

    お願い

    ■このページのURL


    ■このページのタイトル


    ■リンクタグ


    ※リンクして頂いた方でご希望者には貴サイトの紹介記事を作成してリンクを設置します。
    サイト上部の問い合わせよりご連絡ください。
    ただしサイトのジャンルによっては、お断りさせていただくことがあります。