LoginSignup
3
1

動的生成したstyleタグが可能にすること(JavaScriptで要素生成前にCSSを変更)

Last updated at Posted at 2023-12-19

はじめに

動的に生成される要素のCSSをあらかじめ変更しておきたい。といった稀なケースの対応方法。

先に方法を書いてしまうと
「JavaScriptで生成したstyleタグにCSSを書いて、htmlのheadタグに追加して、動的生成にする要素に適用する」原始的な方法です。

稀なケースとは?

例えば「動的生成される要素のCSSを変更したいけど、要素の生成を監視はしない」といったお題があり、以下のような縛りまである場合。

生成する動的要素に対して、

  • 新しいclassやidを追加しない
  • style属性を使ってCSSを書き込まない
  • 外部スタイルシートも修正しない

JavaScriptでよくある要素のCSS変更方法

JavaScriptを使用したCSS変更の多くが、以下のように要素の取得が前提になっています。

JavaScriptでよくある要素のCSS変更
// 要素を取得
var div = document.getElementById('target');
// または下記で取得
// var div = document.querySelector('#target');

// 取得要素のCSSを変更
div.style.backgroundColor = 'red';

生成前の要素は取得できずCSSの設定変更はできないので、styleタグ(style要素)を利用することにします。
headタグ内のstyleタグに書いたCSSは、動的要素の出現時にすぐに適用されるので、先にstyleタグを動的生成してしまいます。

styleタグとCSSの追加方法

動的にstyleタグを追加する方法は以下の3ステップと簡単。

styleタグの追加方法
// 1. styleタグを生成
var styleTag = document.createElement('style'); 

// 2. styleタグ内に追加するCSSを記述
styleTag.textContent = '#target {background-color: #f00;}';

// 3. headタグにstyleタグ追加
document.head.appendChild(styleTag);

createElementでstyleタグを生成してtextContentにCSSを書いたら、appendChildでheadタグ内に追加するだけです。

headタグ内に追加されたら、CSSが適用されます。

追加したstyleタグを削除する場合に備えて要素名.id = 'id名';のようにidを付与しておくと便利です。(例えば、 styleTag.id = 'js_style';)

CSS記述サンプル

styleの記述方法
var styleTag = document.createElement('style'); 
document.head.appendChild(styleTag);


// 1個追加
styleTag.textContent = '#target {background-color: #0f0 !important;}';


// まとめて複数追加(テンプレートリテラル使用)
styleTag.textContent = `
    #target {fonr-size: 10px;}
    #target {border: solid 3px #0f0;}
`;


// 変数とコメントアウトを使う
var s = 20;
styleTag.textContent = `
    /* CSS書式のコメントアウトも使用できる */
    #target {font-size: ${ s }px;}
    #target {border: solid 1px green;}
`;


// += 演算子で追記する
styleTag.textContent += `
	#target {padding: 10px;}
`;

CSSと同じ書式セレクタ名 {プロパティ名: 値;}で書けるところが一目瞭然で良いですね。
ショートハンドで書くこともできます。

テンプレートリテラルを使うと

  • 改行できる(スタイルシートからのコピペも出来て便利)
  • CSS書式のコメントアウトが使える(/* ~ */)
  • 変数を使いたいときは ${ 変数名 } のように書けばOK

のように使い道が広がります。

サンプル

以下は、動的に生成される要素のCSSをあらかじめ変更するサンプル。

  • ボタンを押して1~5個のdivタグを#parent(width300px)内に動的生成
  • 動的生成されるdivタグにはクラス(.child)を付与してあり、widthは100pxに設定している
  • div.childが#parentに横並びでちょうど300pxに収まるように、動的生成前にwidthを計算して生成したstyleタグに書き込み、上書きする
<!DOCTYPE html>
<html>
<head>
    <title>sample</title>
    <style>
    #parent {
    	width: 300px;
    	height: 30px;
    	background-color: #efefef;
    }

    .child {
    	width: 100px;
    }
    </style>
</head>

<body>
	<p>▼ #parent ▼</p>
	<div id="parent"></div>
	<button id="btn">動的に要素を生成</button>

	<script>
	var btn = document.getElementById('btn');
 
	btn.addEventListener('click', function(){

        // 生成したstyleがあれば削除(リセット)
    	var styleTag = document.getElementById('js_style');
		if(styleTag != null){
			styleTag.remove();
		}


		// 1 ~ 5までランダムな整数を生成
		var num = 0;
		num = Math.floor(Math.random() * 5) + 1;

		// 動的にstyleタグを追加(1 ~ 5個)
		var styleTag = document.createElement('style'); // styleタグ生成
		styleTag.id = 'js_style';                       // id付与
        document.head.appendChild(styleTag); 		    // headタグに<style id="js_style">を追加

		// 動的に追加する要素の幅を計算して、あらかじめCSSに設定
		var w = Math.floor(300 / num);
  
		styleTag.textContent = `
			/* styleタグを追加 */
			#parent div.child {
				display: inline-block;
				box-sizing: border-box;
				width: ${ w }px;
				border: solid 1px #000;
			}
		`;


		// 要素を動的に追加
		var target = document.getElementById('parent');
		target.innerHTML = '';
		for (var i = 0; i < num; i++ ) {
			var elem = document.createElement('div');
			elem.classList.add('child');
			elem.textContent = i + 1 + '個目';
			target.appendChild(elem);
		}
	});
	</script>
</body>
</html>

あとがき

「style要素を生成して追加する」といったトリッキーな事をする機会はまず無く、MutationObserverで要素の生成を監視して、CSSを上書きする方法が正攻法かなと思います。そこを敢えて何とかして別の方法はないか??という試みでしたが、やってみると意外に面白かった。(本来なら設計で稀ななケースを回避しておくことが大事かと思います)

3
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1