JavaScriptで複数要素の中から最も広い横幅の値を取得する方法

Webサイトのデザインにおいて、要素に含まれるコンテンツの量が必ずしも均一であるとは限りません。そのような場合でも、左揃えを基本としたレイアウトで視覚的な一貫性を保つことはよく行われます。しかし、デザインの意図やコンテンツの特性によっては、個々の要素が持つ最大の幅を考慮してレイアウトを調整する必要がでてきます。
WordPressをはじめとするCMS(コンテンツ管理システム)を利用している場合でも、投稿される記事やコンテンツの量が変動的であることが一般的です。もし、これらの要素に対して固定された幅でCSSなどのスタイルを指定してしまうと、コンテンツ量が想定以上に多くなった際に、レイアウトが崩れてしまうという問題が発生します。
このような課題は、HTMLとCSSのみで実装することは難しいですが、JavaScriptを活用することで、変動するコンテンツ量に合わせたレイアウト調整が可能になります。
ここでは、JavaScriptで複数要素の中から最も広い横幅の値を取得する方法についてご紹介します。
最も広い横幅を取得することで、その値をCSSに適用することができます。
複数要素の中から最も広い横幅の値を取得する
手軽にコンテンツを追加できるCMSの利用も多いので、項目の順序なしリストを表すul要素や、説明リストを表すdl要素を例に見ていきます。
サンプルとして、
まずはリスト要素(ulタグ)において、各リスト項目(liタグ)の先頭にあるspanタグ(番号やラベルなど)の幅を揃え、その後のテキストがきれいに縦に並ぶようなレイアウトを実装してみます。
HTMLの構造は、以下のように構築します。
HTML
<ul class="sample-list">
<li><span>a-1</span>Text... text... text... text...</li>
<li><span>a-2.</span>Text... text... text... text...</li>
<li><span>b-18.</span>Text... text... text... text...</li>
<li><span>b-116.</span>Text... text... text... text...</li>
<li><span>c-52.</span>Text... text... text... text...</li>
<li><span>c-67.</span>Text... text... text... text...</li>
</ul>
「ul class=”sample-list”」順序なしリスト全体を定義し、CSSやJavaScriptでスタイルを適用するためのクラス名 「sample-list」を付与しておきます。
span要素では、各リスト項目の先頭に”a-1″, “a-2.”, “b-18.”などの番号や記号を入れ、span要素の後ろのテキストは、各リスト項目の文章としています。
続いてはCSS。
CSS
.sample-list {
list-style: none;
}
.sample-list li {
display: grid;
grid-template-columns: auto 1fr;
align-items: baseline;
}
.sample-list span {
margin: 0 0.3rem 0 0;
}
「.sample-list」は、リストのデフォルトのマーカー(通常は黒い点)を消しています。
「.sample-list li」では、display: gridでグリッドコンテナとし、spanと後続のテキストを別々のグリッドセルとして配置します。
「grid-template-columns: auto 1fr」は、グリッドの列を定義します。autoの1列目の幅は、その内容(spanのテキスト)に合わせて自動で決まります。そして1frの2列目(後続のテキスト)の幅は、利用可能な残りのスペースをすべて使います。
「align-items: baseline」では、span内のテキストと後続のテキストのベースライン(文字の下端の揃うライン)を合わせます。これにより、高さが異なる場合でもテキストがきれいに見えます。
「.sample-list span」は、span要素(番号部分)とその右側のテキストの間に少し隙間を空ける調整となります。
HTMLとCSSのみですと、span要素のコンテンツ量の違いがある場合、すべてのコンテンツが左に詰めて表示されます。

番号部分のspan要素は、コンテンツ量に合わせて、最も広い横幅に自動で調整できると便利です。
続いて、JavaScriptのプログラムを見ていきましょう。
JavaScriptでは、最も広い横幅を取得して、CSSに適用させるプログラムを実装します。
JavaScript
document.addEventListener("DOMContentLoaded", () => {
const spans = document.querySelectorAll(".sample-list span");
const widths = Array.from(spans).map(span => span.offsetWidth);
const maxW = Math.max(...widths);
spans.forEach(span => {
span.style.width = `${maxW}px`;
span.style.whiteSpace = "nowrap";
span.style.wordBreak = "normal";
});
});
「document.addEventListener(“DOMContentLoaded”, () => { … });」は、HTMLドキュメントが完全に読み込まれた後(画像などの読み込み完了前)に、中のコードを実行します。これにより、HTML要素が確実に存在してから処理を開始することができます。
「document.querySelectorAll(“.sample-list span”)」では、sample-listクラス内のすべてのspan要素を取得し変数で管理します。
「Array.from(spans).map(span => span.offsetWidth)」では、取得した各span要素について、実際に画面上で描画されている幅(offsetWidth)を取得し、それらの幅の数値からなる配列を作成します。
「Math.max(…widths)」では、作成した幅の配列の中から、最も大きい値(最大の幅)を探して変数で管理します。
そして、「spans.forEach(span => { … });」で、取得したすべてのspan要素に対して、最も大きい横幅の値を適用させます。
「span.style.width = ${maxW}px
;」で、各span要素のCSSのwidthプロパティを、先ほど見つけた最大の幅 (maxW) ピクセルに設定します。これにより、すべてのspan要素の幅が統一されます。
「span.style.whiteSpace = “nowrap”;」で、span要素内のテキストが、指定した幅を超えても折り返さないようにします。
また、「span.style.wordBreak = “normal”;」では、テキストの改行ルールを通常(単語単位)に設定します。「white-space: nowrap」と併用することで、意図しない改行を防ぎます。
以下、実装結果になります。

全体の流れとしましては、CSSで基本的なレイアウト(番号とテキストを横並び、ベースライン揃え)を行い、JavaScriptでページ読み込み後にすべての番号(span)の中から一番幅が広いものを見つけ、その幅にすべての番号(span)の幅を強制的に合わせるといった感じです。
これで、”a-1”や”b-116.”のように文字数が異なる場合でも、span要素の幅が揃うため、その後のテキストの開始位置がきれいに縦一列に揃い、リスト全体が見やすくなります。
表示・非表示の動作を含む場合
ボタンをクリックしてから表示されるコンテンツであったり、アコーディオンなどの開閉式で非表示にしているコンテンツを展開して表示する場合は、要素の非表示の影響で幅を取得できない場合があります。
要素は残しつつ非表示にする「visibility: hidden;」で表示を調整しているのであれば問題ないですが、要素ごと非表示にする「display: none;」で表示を調整している場合は、要素の幅を取得することができないので、クリックしてから幅を取得するような流れで処理を実装する必要があります。
サンプルとして、ボタンをクリックしてコンテンツを表示する場合で見ていきます。
HTMLでは、先ほどのコードからbutton要素を追加しています。
HTML
<button class="list-btn">List</button>
<ul class="sample-list">
<li><span>a-1</span>Text... text... text... text...</li>
<li><span>a-2.</span>Text... text... text... text...</li>
<li><span>b-18.</span>Text... text... text... text...</li>
<li><span>b-116.</span>Text... text... text... text...</li>
<li><span>c-52.</span>Text... text... text... text...Text... text... text... text...Text... text... text... text...Text... text... text... text...Text... text... text... text...Text... text... text... text...</li>
<li><span>c-67.</span>Text... text... text... text...</li>
</ul>
追加したbutton要素では、ボタンをクリックしてリストを表示・非表示をできるようにします。クラス名「list-btn」がJavaScriptでの操作に使われます。
続いてはCSS。
「.sample-list」にdisplay: none;を追加し、ページが最初に読み込まれたとき、初期状態では画面に表示されないようにします。
CSS
.sample-list {
list-style: none;
display: none;
}
.sample-list li {
display: grid;
grid-template-columns: auto 1fr;
align-items: baseline;
}
.sample-list span {
margin: 0 0.3rem 0 0;
}
button {
margin: 1rem 0;
padding: 0.25rem 0.625rem;
}
全体的なスタイルは、先ほどと変わりはありませんが、追加された「button」には、ボタン要素に対する基本的なスタイル(上下の余白と内側のパディング)を設定しておきます。
続いてはJavaScriptです。
以下、サンプルコードになります。
JavaScript
document.addEventListener("DOMContentLoaded", () => {
const listBtn = document.querySelector(".list-btn");
const sampleList = document.querySelector(".sample-list");
listBtn.addEventListener("click", () => {
if (sampleList.style.display === "none" || sampleList.style.display === "") {
sampleList.style.display = "block";
const spans = sampleList.querySelectorAll("span");
const widths = Array.from(spans).map(span => span.offsetWidth);
const maxW = Math.max(...widths);
spans.forEach(span => {
span.style.width = `${maxW}px`;
span.style.whiteSpace = "nowrap";
span.style.wordBreak = "normal";
});
} else {
sampleList.style.display = "none";
}
});
});
ボタン(listBtn)とリスト(sampleList)の要素を取得し、ボタンにクリックイベントリスナーを追加して、クリックされたときに特定の処理を実行するようにします。
まず、リスト(sampleList)が現在非表示(display: none)かどうかを確認します。
「sampleList.style.display === “”」は、インラインスタイルでdisplayが設定されていない場合の初期状態(CSSでnoneになっている)を考慮しています。
次にif文にて、もし非表示なら「sampleList.style.display = “block”;」でリストを表示状態にして、リストが表示された後で、リスト内のすべてのspan要素を取得し、それらのoffsetWidth(実際の描画幅)を計算します。その後に最大の幅を見つけ、すべてのspan要素の幅をその最大幅に揃えるスタイルを適用させます。
そして、もし表示されているなら、「sampleList.style.display = “none”;」でリストを非表示にします。
以下、実装結果になります。

重要な点として、display: none;の状態では要素の幅(offsetWidth)を正しく取得できないため、必ずdisplay: block;などで表示状態にしてから幅の計算とスタイルの適用を行う必要があります。
これで開閉式のコンテンツでも、要素の幅を計算して最も広い横幅の値を取得し、スタイルに適用させることができます。
dl要素の構造での最も広い横幅の値の取得
dl要素の説明リストを表す構造でも同じように、取得した複数のdt要素から最も広い幅を取得してスタイルに適用させます。
以下、HTMLのサンプルコードです。
dl要素で、用語(dt)とその説明(dd)を記述するための定義リストを構築します。
HTML
<dl class="definition-list">
<dt>a-1</dt>
<dd>Text... text... text... text...</dd>
<dt>a-2.</dt>
<dd>Text... text... text... text...</dd>
<dt>b-18.</dt>
<dd>Text... text... text... text...</dd>
<dt>b-116.</dt>
<dd>Text... text... text... text...</dd>
<dt>c-52.</dt>
<dd>Text... text... text... text... Text... text... text... text... Text... text... text... text... Text... text... text... text... Text... text... text... text...</dd>
<dt>c-67.</dt>
<dd>Text... text... text... text...</dd>
</dl>
dl要素に「class=”definition-list」というクラス名を付与して、CSSでスタイルを適用したり、JavaScriptでこのリスト要素を特定したりするために使います。
続いてCSS。
CSS
.definition-list,
.definition-list dt,
.definition-list dd {
margin: 0;
padding: 0;
}
.definition-list {
display: grid;
grid-template-columns: auto 1fr;
column-gap: 0.6rem;
row-gap: 0.5rem;
}
.definition-list dt {
grid-column: 1;
box-sizing: border-box;
align-self: baseline;
}
.definition-list dd {
grid-column: 2;
align-self: baseline;
}
最初に、dl, dt, dd が持つブラウザ標準の余白(マージン、パディング)をゼロにしておきます。
「.definition-list」では、display: gridでdl要素自体をグリッドレイアウトのコンテナ(親要素)にして、中の dt と dd を格子状に配置できます。
「grid-template-columns: auto 1fr;」では、グリッドの列構成を定義しています。
autoの1列目(dtが入る)の幅は、各行の dt 要素の内容(テキスト)に応じて自動的に決まります。1frの2列目(ddが入る)は、利用可能な残りの幅をすべて使います。
column-gap, row-gap は列の間、行の間の隙間を設定しています。
「.definition-list dt」と「.definition-list dd」では、grid-columnでそれぞれをグリッドの1列目、2列目に配置します。
また、align-self: baselineで、同じ行にある dt と dd のテキストのベースライン(文字の下端が揃う線)を合わせます。
続いてJavaScript。
JavaScriptは、ul要素の場合とほとんど変わりわなく、「document.querySelector(“.definition-list”)」でdl要素を対象に、「staticList.querySelectorAll(“dt”)」でdt要素を取得して、最大の幅を求めた後にスタイルを適用する流れとなっています。
JavaScript
document.addEventListener("DOMContentLoaded", () => {
const staticList = document.querySelector(".definition-list");
if (staticList) {
const dts = staticList.querySelectorAll("dt");
if (dts.length > 0) {
const widths = Array.from(dts).map(dt => dt.offsetWidth);
const maxW = Math.max(...widths);
dts.forEach(dt => {
dt.style.width = `${maxW}px`;
dt.style.whiteSpace = "nowrap";
dt.style.overflow = "hidden";
dt.style.textOverflow = "ellipsis";
// dt.style.wordBreak = "normal";
});
}
}
});
少し変更している部分は、if文でdl要素があるか、またdt要素があるかをチェックして処理を実行するようにしています。
なくても動作しますが、省略しない方が安全ということもあります。
dl要素での表示・非表示の動作を含む場合
dl要素での表示・非表示の開閉式でも、ul要素と同じく、ボタンをクリックして要素を表示した後に、複数のdt要素から最大幅を求めてスタイルを適用する流れとなります。
サンプルコードを一気に見ていきます。
まずはHTML。
HTML
<button class="list-btn">List</button>
<dl class="definition-list">
<dt>a-1</dt>
<dd>Text... text... text... text...</dd>
<dt>a-2.</dt>
<dd>Text... text... text... text...</dd>
<dt>b-18.</dt>
<dd>Text... text... text... text...</dd>
<dt>b-116.</dt>
<dd>Text... text... text... text...</dd>
<dt>c-52.</dt>
<dd>Text... text... text... text... Text... text... text... text... Text... text... text... text... Text... text... text... text... Text... text... text... text...</dd>
<dt>c-67.</dt>
<dd>Text... text... text... text...</dd>
</dl>
JavaScriptでクリックされたときのイベント処理を実装するため、「list-btn」というクラス名を付与したbutton要素を追加します。
他、dl要素の構造はそのままとなっています。
続いてCSS。
dl要素の dt と dd に対してのスタイルに、追加したbutton要素に対するスタイルも適用させます。
CSS
.list-btn {
margin-bottom: 1rem;
padding: 0.5rem 1rem;
cursor: pointer;
}
.definition-list,
.definition-list dt,
.definition-list dd {
margin: 0;
padding: 0;
}
.definition-list {
display: none;
grid-template-columns: auto 1fr;
column-gap: 0.6rem;
row-gap: 0.5rem;
}
.definition-list.is-open {
display: grid;
}
.definition-list dt {
grid-column: 1;
box-sizing: border-box;
align-self: baseline;
}
.definition-list dd {
grid-column: 2;
align-self: baseline;
}
初期状態では非表示にしますので、「.definition-list」ではdisplay: noneを指定しますが、表示した際にグリッドレイアウトを適用させる必要があるので、「.definition-list.is-open」でdisplay: gridを指定しています。
「is-open」のクラス名は、JavaScriptで付与します。
最後にJavaScript。
JavaScript
document.addEventListener("DOMContentLoaded", () => {
const listBtn = document.querySelector(".list-btn");
const definitionList = document.querySelector(".definition-list");
if (listBtn && definitionList) {
const alignDtWidths = (listElement) => {
const dts = listElement.querySelectorAll("dt");
if (dts.length > 0) {
dts.forEach(dt => { dt.style.width = ''; });
requestAnimationFrame(() => {
const widths = Array.from(dts).map(dt => dt.offsetWidth);
const validWidths = widths.filter(w => w > 0);
if (validWidths.length === 0) return;
const maxW = Math.max(...validWidths);
dts.forEach(dt => {
dt.style.width = `${maxW}px`;
dt.style.whiteSpace = "nowrap";
dt.style.overflow = "hidden";
dt.style.textOverflow = "ellipsis";
});
});
}
};
listBtn.addEventListener("click", () => {
const isOpen = definitionList.classList.contains("is-open");
if (isOpen) {
definitionList.classList.remove("is-open");
} else {
definitionList.classList.add("is-open");
alignDtWidths(definitionList);
}
});
}
});
dl要素の取得やdt要素の取得の部分は同じような処理になります。
「listBtn = document.querySelector(“.list-btn”)」で、今回追加したbutton要素を取得しています。
「if (listBtn && definitionList) { … }」は、listBtn と definitionList の両方の要素がHTML内にちゃんと存在するかを確認した後に処理を実行するようにしています。
「const alignDtWidths = (listElement) => { … }」で、alignDtWidths という名前の関数を定義しています。
この関数は、引数として受け取ったリスト要素(listElement)の中のdt要素の幅を揃える役割を持ちます。
複数のdt要素を取得して、最大の幅を求めてからスタイルを適用する処理になります。
「listBtn.addEventListener(“click”, () => { … })」では、listBtn(ボタン要素)がクリックされたときに { … } 内の処理を実行するように設定します。
「const isOpen = definitionList.classList.contains(“is-open”);」で、definitionList(dl要素)が現在 is-openというCSSクラスを持っているかどうかを確認します。このクラスの有無で、リストが開いているか閉じているかを判断します(CSS側で.is-openクラスが付いたときにリストを表示するようなスタイル定義が必要)
「if (isOpen)」で、もしリストが開いている(is-openクラスがある)場合は、「definitionList.classList.remove(“is-open”);」でis-openクラスを削除します。これにより、CSSのスタイルが切り替わり、リストを非表示にします。
そして、「else」でもしリストが閉じている(is-openクラスがない)場合は、「definitionList.classList.add(“is-open”);」で、is-openクラスを追加します。これにより、CSSのスタイルが切り替わり、リストを表示します。
「alignDtWidths(definitionList);」で、リストが表示された直後に、上で定義したalignDtWidths関数を呼び出して、dt要素の幅を揃える処理を実行します。
これで、dl要素で構築した定義リストの開閉コンテンツで、dt要素の幅を最大の幅に合わせて揃えることができます。
最後に
リスト表示のレイアウトでコンテンツ幅を揃えたい場合、CSSで固定の数値を指定してしまうと、コンテンツ量が増加したときに表示崩れを起こすことがありますが、JavaScriptを利用することで、幅を計算して自動で調整するようにできます。
今回ご紹介したプログラムの流れとしては、対象の要素(複数の要素)を取得して、その中からもっとも広い幅を探して、対象の要素すべてに最大の幅のスタイルを適用する流れになります。
また、用語(dt)にあたる文章があまりにも長くなるようでしたら、MAX幅を指定する処理を追加することで、説明との幅のバランスを保つことができるでしょう。
必要な時は、ぜひ参考にしてください。