質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
foreach

foreachは、List・Collection・Arrayといったデータ構造の各要素に対して繰り返し処理を実行するために扱われる、制御構造の構文です。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

3回答

1374閲覧

[javascript]文字数をカウントしてはみ出した部分を「...」で表示させたい

ponta9001

総合スコア2

foreach

foreachは、List・Collection・Arrayといったデータ構造の各要素に対して繰り返し処理を実行するために扱われる、制御構造の構文です。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

1グッド

1クリップ

投稿2023/05/07 18:26

編集2023/05/08 08:25

前提

①「width:80%、font-size:14px」内に全角で何文字収まるか調べるcapasity関数を作成し、
②対象となるテキストの文字数をカウントするtextcount関数を作成、
③はみ出した部分を「...」で表示するshorten関数を作成
④ページ読み込み時にshorten関数を実行

というプログラムをネットのものを参考にしつつ自分のサイトに合うように改変した以下のソースコードをまず見て頂けますでしょうか。

該当のソースコード

html

1<style type="text/css"> 2 .sample { 3 width: 80%; 4 font-size: 14px; 5 border-style: solid; 6 margin-top: 30px; 7 } 8 9 .sample * { 10 display: inline-block; 11 } 12 13 .sample div { 14 background-color: yellow; 15 } 16 17 .sample span { 18 background-color: pink; 19 } 20</style> 21<body> 22 <div class="sample"> 23 <div>.sampleに何文字ならキレイに収まるか調べて、</div> 24 <span>文字数が多い場合.sample > div部分を省略して表示させる</span> 25 </div> 26 <div class="sample"> 27 <div>実現させたいのはページ内にあるすべての</div> 28 <span>.sample > divに同じ処理をさせたい</span> 29 </div> 30<script type="text/javascript"> 31/*全角で最大何文字ならキレイに収まるかを調べる関数*/ 32function capacity(s) { 33 34 //算出されたCSSを取得 35 var style = window.getComputedStyle(s); 36 37 //フォントサイズとボックスの幅を数値で取得 38 var fontsize = parseInt(style.fontSize); 39 var width = parseInt(style.width); 40 41 //ボックスに収納できる文字数 42 var mojisuu = Math.floor(width/fontsize); 43 44 return mojisuu; 45}; 46 47 48/*対象のテキスト(.sample)が全角1文字、半角0.5文字で合計何文字になるか計算する関数*/ 49function textcount(n) { 50 51 var nagasa = 0; 52 53 for (i = 0; i < n.length; i++) { 54 55 if(n[i].match(/[ -~]/) ) { 56 57 nagasa += 0.5; 58 } 59 60 else { 61 62 nagasa += 1; 63 } 64 65 66 } 67 68 return nagasa; 69 70}; 71 72 73var sample = document.querySelector(".sample"); 74var str = sample.innerText; 75var strfirst = sample.firstElementChild.innerHTML; 76 77/**/ 78function shorten() { 79 80 var kstr = str; 81 var count = (textcount(kstr)); 82 83 if (count > capacity(sample)) { 84 85 //はみ出してる文字数+4文字をテキストの後ろから削除 86 strfirst = strfirst.slice( 0, capacity(sample) - count -4 ); 87 88 //「…」と連結し、元のテキストを置き換える 89 sample.firstElementChild.innerHTML = strfirst + "…"; 90 } 91 92 //要素の幅が十分にあればそのまま表示 93 else { 94 ; 95 } 96}; 97 98shorten(); 99 100</script> 101</body>

発生している問題・試したこと・実現したいこと

上のプログラムだと73行目でquerySelector()メソッドを使用しているため最初の.sampleしか省略されません。

すべての.sampleの文字数を調べ、それぞれの.sampleの子要素のdivを省略させるにはどうすればいいでしょうか...?

querySelectorAll()メソッドとforEach()メソッドの反復処理を使ったりして試行錯誤してみたのですが根本的な使い方が間違ってるのか前に進めず、迷って質問させて頂きました。

どうぞよろしくお願いします。

上記のコードをforEach文で書き直したコード

html

1<script> 2<!--コメントを受け上記コードの73行目以降をforEach文で修正したものです-> 3var sample = document.querySelector(".sample"); 4var sampleall = document.querySelectorAll(".sample"); 5 6 7sampleall.forEach(function(value){ 8 9 var count = (textcount(value.innerText)); 10 11 if (count > capacity(value)) { 12 13 //はみ出してる文字数+4文字をテキストの後ろから削除 14 value.firstElementChild.innerHTML = value.firstElementChild.innerHTML.slice( 0, capacity(value) - count -4 ); 15 16 //「…」と連結し、元のテキストを置き換える 17 value.firstElementChild.innerHTML = value.firstElementChild.innerHTML + "…"; 18 } 19 20 //要素の幅が十分にあればそのまま表示 21 else { 22 ; 23 } 24}); 25</script> 26
hatena19👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

int32_t

2023/05/07 20:34

querySelectorAll()とforEach()を使う方針で間違っていません。それらを使ってうまくいかなかったコードを開示してください。
退会済みユーザー

退会済みユーザー

2023/05/07 22:05

対象を div 要素だけにするというのはダメですか? それでよければ、テキストが領域をこえた場合に「…」を表示する text-overflow:ellipsis が使えそうですが。
yambejp

2023/05/08 00:26

「.sample」にHTMLタグがはいるとなるとぴったり収まる文字数は推定できないですね。くわえて表示上の文字と実際の文字は異なります。たとえば「>」と記載していますがHTML的には「&gt;」など4文字で記載します。スペースを続けても1文字分しか表示されなかったりいろいろあるので、どこまでの対応が必要かよくよく検討されたほうがよいでしょう
ponta9001

2023/05/08 08:00

>int32_tさん コメントありがとうございます。 誤ってコードを消してしまったため先ほど書き直したのですが、すると理想通りの動きをしてくれました!そのコードを質問の最下部に追記しました。forEach()を使うのは初めてだったためもしよければおかしな点がないか確認いただけないでしょうか?
ponta9001

2023/05/08 08:06

> yambejpさんコメントありがとうございます。 おっしゃる通り参考にしたサイトにも絵文字や記号が含まれると誤差が出ると書いてありました!実装するサイトの該当テキストでは現状絵文字と記号は使ってないため考えていませんでしたが想定して対応を考えようと思います。
int32_t

2023/05/08 21:36

更新後のコードで動作は期待通りだと思います。 細かいことを言うなら、 * インデントがおかしい * (textcount(value.innerText)) を囲む () は不要 * innerHTML は読むのも書くのも重たい処理なので、一度の代入で済ませるべき。〜.innerHTML = 〜.innerHTML.slice(〜) + "…"; * else に書くことがないなら else { ; } は不要
退会済みユーザー

退会済みユーザー

2023/05/09 02:59

> 該当テキストでは現状絵文字と記号は使ってない それらは除くとしても、質問に書かれた以下のコードではプロポーショナルフォントやサロゲートペアへの対応が考えられてないところに問題があるのではないですか? > //ボックスに収納できる文字数 > var mojisuu = Math.floor(width/fontsize); > /*対象のテキスト(.sample)が全角1文字、半角0.5文字で合計何文字になるか計算する関数*/ > function textcount(n) { > ...
退会済みユーザー

退会済みユーザー

2023/05/11 03:40

質問者さん、その後無言ですが、コメントや回答への追記があるのでそれらに対するフィードバックを返してください。役に立った/立たなかったぐらいはすぐに返せるのでは?
guest

回答3

0

質問のコメントに対する返事がないのでいろいろ不明ですが・・・

対象を div 要素にし、それにテキストが領域をこえた場合に「…」を表示する text-overflow:ellipsis を使った例を書いておきます。

html

1<!DOCTYPE html> 2 3<html xmlns="http://www.w3.org/1999/xhtml"> 4<head runat="server"> 5<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 6 <title></title> 7 <style type="text/css"> 8 .style1 9 { 10 width: 80%; 11 overflow: hidden; 12 white-space: nowrap; 13 text-overflow: ellipsis; 14 } 15 16 .sample1 17 { 18 width: 400px; 19 } 20 21 .sample2 22 { 23 width: 200px; 24 } 25 </style> 26</head> 27<body> 28 <div class="sample1"> 29 <div class="style1">.sampleに何文字ならキレイに収まるか調べて、</div> 30 <div class="style1"><span>文字数が多い場合.sample &gt; div部分を省略して表示させる</span></div> 31 </div> 32 <div class="sample2"> 33 <div class="style1">実現させたいのはページ内にあるすべての</div> 34 <div class="style1"><span>.sample &gt; divに同じ処理をさせたい</span></div> 35 </div> 36</body> 37</html>

結果は以下の通りです。

イメージ説明

上はブラウザに Windows 10 の Chrome 113.0.5672.64 を使ったものです。

テキストが領域をこえた場合に三点リーダー「…」を表示する text-overflow:ellipsis はもともと IE の独自拡張だそうですが、他のブラウザでも取り入れられているようで、以下のブラウザで同様な結果となります。

Edge 113.0.1774.35, Chrome 113.0.5672.64, Firefox 112.0.2, Opera 98.0.4759.15


【追記」

下のコメント欄の 2023/05/09 12:05 の私のコメントで「質問に書かれた JavaScript のコードではプロポーショナルフォントやサロゲートペアへの対応が考えられてないところに問題があるのではないですか? あとで、どういう問題がありそうか回答欄に追記しておきます」と書いた件です。

どのような問題がありそうかの具体例を書いておきます。以下のコードを見てください。body 要素の中にある br 要素の上の方を最初の回答に書いたように CSS の text-overflow:ellipsis を使って、br 要素の下の方を質問者さんが書いた JavaScript で処理してみます。

html

1<!DOCTYPE html> 2 3<html xmlns="http://www.w3.org/1999/xhtml"> 4<head runat="server"> 5<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 6 <title></title> 7 <style type="text/css"> 8 .style1 9 { 10 width: 80%; 11 overflow: hidden; 12 white-space: nowrap; 13 text-overflow: ellipsis; 14 } 15 16 .style2 17 { 18 width: 80%; 19 } 20 21 .sample1 22 { 23 width: 400px; 24 } 25 26 .sample2 27 { 28 width: 200px; 29 } 30 </style> 31 <script type="text/javascript"> 32 function capacity(s) { 33 34 //算出されたCSSを取得 35 var style = window.getComputedStyle(s); 36 37 //フォントサイズとボックスの幅を数値で取得 38 var fontsize = parseInt(style.fontSize); 39 var width = parseInt(style.width); 40 41 //ボックスに収納できる文字数 42 var mojisuu = Math.floor(width / fontsize); 43 44 return mojisuu; 45 }; 46 47 function textcount(n) { 48 var nagasa = 0; 49 for (i = 0; i < n.length; i++) { 50 if (n[i].match(/[ -~]/)) { 51 52 nagasa += 0.5; 53 } 54 55 else { 56 57 nagasa += 1; 58 } 59 } 60 61 return nagasa; 62 }; 63 64 window.addEventListener("DOMContentLoaded", () => { 65 var sampleall = document.querySelectorAll(".style1"); 66 sampleall.forEach(function (value) { 67 var count = (textcount(value.innerText)); 68 var cap = capacity(value); 69 console.log(`count: ${count}, capacity: ${cap}`); 70 }); 71 72 var sampleall2 = document.querySelectorAll(".style2"); 73 sampleall2.forEach(function (value) { 74 var count = (textcount(value.innerText)); 75 76 if (count > capacity(value)) { 77 78 //はみ出してる文字数+4文字をテキストの後ろから削除 79 value.innerHTML = value.innerHTML.slice(0, capacity(value) - count - 4); 80 81 //「…」と連結し、元のテキストを置き換える 82 value.innerHTML = value.innerHTML + "…"; 83 } 84 }); 85 }); 86 </script> 87</head> 88<body> 89 <div class="sample1"> 90 <div class="style1">エスケープされた &lt; &gt; &amp; などの文字の扱いはどうなる?</div> 91 <div class="style1">Proportional Font WWWWWWWWWWW iiiiiiii のように文字幅が異なるフォント</div> 92 <div class="style1">サロゲートペア 𠀋 𡈽 𠮟 などはどのようになるか?</div> 93 <div class="style1">絵文字 🍎 🍏 👨‍🌾 などはどのようになるか?</div> 94 </div> 95 96 <br /> 97 98 <div class="sample1"> 99 <div class="style2">エスケープされた &lt; &gt; &amp; などの文字の扱いはどうなる?</div> 100 <div class="style2">Proportional Font WWWWWWWWWWW iiiiiiii のように文字幅が異なるフォント</div> 101 <div class="style2">サロゲートペア 𠀋 𡈽 𠮟 などはどのようになるか?</div> 102 <div class="style2">絵文字 🍎 🍏 👨‍🌾 などはどのようになるか?</div> 103 104 </div> 105</body> 106</html>

結果は以下のようになります。上が CSS の text-overflow:ellipsis での処理、下が質問者さんが書いた JavaScript での処理です。ブラウザは Chrome 113.0.5672.93、フォントは 16px Meiryo です。

イメージ説明

該当テキストでは現状絵文字と記号は使ってない

・・・とのことですので絵文字の結果は関係ない、&lt; &gt; &amp; などの文字は length では 1 文字としてカウントされるので問題ないと思いますが、プロポーショナルフォントやサロゲートペアはどうでしょう?

問題は質問のコードの、

//ボックスに収納できる文字数
var mojisuu = Math.floor(width/fontsize);

/対象のテキスト(.sample)が全角1文字、半角0.5文字で合計何文字になるか計算する関数/
function textcount(n) {
...
for (i = 0; i < n.length; i++) {

・・・のあたりから来ているようですが、そこを改善して何とかするというのは JavaScript では無理っぽい気がします。

CSS の text-overflow:ellipsis と JavaScript を組み合わせて何とかなりそうな気がします。が、質問者さんのやりたいことが下のコメント欄の説明を見ても分かりませんので、気がするだけですけど。

投稿2023/05/08 01:10

編集2023/05/09 03:51
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ponta9001

2023/05/08 07:42

返事が遅れてすみません。返事がないなか質問と回答頂きありがとうございます。 自分でも試行錯誤するなかでtext-overflow:ellipsisを使う場合2つ課題が発生したためjsを使うことにしました。 1つはSurferOnWwwさんの書いて頂いた例でいうstyle1の2つの子要素はどちらもinline-block要素で1行にしたい→かつspanで囲ってるほうは省略させたくないのでwidthの指定はしない→span要素の長さ分だけdiv要素の長さを省略させる方法が分からなかったという点です。 正直自分は初心者のほうなのでもし知識足らずでおかしなことを言っていたら申し訳ありません。 もう1つの問題は、実は実際に作成しようとしてるものは <div class="sample1"> <div class="style1"><!--この中のテキストを「こけくきかおえういあ」の順で省略--> <div>あいうえお</div> <span>かきくけこ</span> </div> <span>このテキストは省略しない</span> </div> という構成で、sample1にtext-overflow:ellipsisを使用すると1文字ずつ「...」に省略されるのではなく丸々省略されてしまった点です。 cssだけで実装できたら嬉しかったのですが自分では方法が分からずjsに頼りました。
退会済みユーザー

退会済みユーザー

2023/05/09 03:05

上の質問者さんのコメントの説明では何をしたいのかよくわかりませんが、何にせよ、質問に書かれた JavaScript のコードではプロポーショナルフォントやサロゲートペアへの対応が考えられてないところに問題があるのではないですか? あとで、どういう問題がありそうか回答欄に追記しておきます。
guest

0

自己解決

たくさんのご回答ありがとうございました!
querySelectorAll()とforEach()を使う方針で正しいというコメントを頂いた後、自分で書き直したコードで期待通りの動作をしたため自己解決とさせていただきます。

以下変更後のコードです。(73行目までは同じです)

動作

ページ内にある同じclass要素のテキストの文字数をカウントし、はみ出してる(1行収めたいけど2行になる)部分をそれぞれ「...」で表示するプログラムです。

自分の場合テキストの途中を省略させたかったので、テキストの末尾を「...」に変更したい場合は下記コード85行目の「value.firstElementChild.innerHTML」を「value.innerHTML」に変更させてください。

注意

このプログラムではコメント頂いた複数の問題点解消できておらず、以下のテキストを含む場合は文字数のカウントに誤差が発生し理想通りに省略されない場合があります。
・絵文字
・記号
・特殊文字
・プロポーショナルフォント
・サロゲートペア......等々

全角(幅が高さと同じ)と半角(幅が高さの1/2)で構成されたテキストの場合は期待通りの動作をすると思います。

html

1 2<style type="text/css"> 3 .sample { 4 width: 80%; 5 font-size: 14px; 6 border-style: solid; 7 margin-top: 30px; 8 } 9 10 .sample * { 11 display: inline-block; 12 } 13 14 .sample div { 15 background-color: yellow; 16 } 17 18 .sample span { 19 background-color: pink; 20 } 21</style> 22<body> 23 <div class="sample"> 24 <div>.sampleに何文字ならキレイに収まるか調べて、</div> 25 <span>文字数が多い場合.sample > div部分を省略して表示させる</span> 26 </div> 27 <div class="sample"> 28 <div>実現させたいのはページ内にあるすべての</div> 29 <span>.sample > divに同じ処理をさせたい</span> 30 </div> 31<script type="text/javascript"> 32/*全角で最大何文字ならキレイに収まるかを調べる関数*/ 33function capacity(s) { 34 35 //算出されたCSSを取得 36 var style = window.getComputedStyle(s); 37 38 //フォントサイズとボックスの幅を数値で取得 39 var fontsize = parseInt(style.fontSize); 40 var width = parseInt(style.width); 41 42 //ボックスに収納できる文字数 43 var mojisuu = Math.floor(width/fontsize); 44 45 return mojisuu; 46}; 47 48 49/*対象のテキスト(.sample)が全角1文字、半角0.5文字で合計何文字になるか計算する関数*/ 50function textcount(n) { 51 52 var nagasa = 0; 53 54 for (i = 0; i < n.length; i++) { 55 56 if(n[i].match(/[ -~]/) ) { 57 58 nagasa += 0.5; 59 } 60 61 else { 62 63 nagasa += 1; 64 } 65 66 67 } 68 69 return nagasa; 70 71}; 72 73 74var sample = document.querySelector(".sample"); 75var sampleall = document.querySelectorAll(".sample"); 76 77 78sampleall.forEach(function(value){ 79 80 var count = textcount(value.innerText); 81 82 if (count > capacity(value)) { 83 84 //はみ出してる文字数+4文字をテキストの後ろから削除 85 value.firstElementChild.innerHTML = value.firstElementChild.innerHTML.slice( 0, capacity(value) - count -4 ) + "…"; 86 } 87 //要素の幅が十分にあればそのまま表示 88}); 89</script> 90</body>```

投稿2023/05/11 14:13

ponta9001

総合スコア2

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

「spanの方は省略させたくない」という記述がありましたが、仕様がよくわかりません。もう少しきっちり命題で説明したほうが良いと思います。上記単純に反映させる(divは...で省略、spanは溢れたら隠すだけ)ならこんな感じ

CSS

1<style> 2.sample { 3 width: 10%; /*文字が隠れるよう適当な長さで調整*/ 4 font-size: 14px; 5 border-style: solid; 6 margin-top: 30px; 7 overflow:hidden; 8} 9.sample > * { 10 max-width: 100%; 11 white-space:nowrap; 12 overflow:hidden; 13 text-overflow:ellipsis; 14} 15</style> 16<div class="sample"> 17<div>.sampleに何文字ならキレイに収まるか調べて、</div> 18<span>文字数が多い場合.sample > div部分を省略して表示させる</span> 19<div>test</div> 20</div> 21<div class="sample"> 22<div>実現させたいのはページ内にあるすべての</div> 23<span>.sample > divに同じ処理をさせたい</span> 24<span>test</span> 25</div>

投稿2023/05/09 00:40

yambejp

総合スコア114839

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問