Blind/Air

Atmosphere's Web-Log

- Personal Animation and Motion Graphics Works -

このTop画像は東日本大震災とか色々あって制作途中で頓挫した幻の「恋色空模様」リメイクOPです。

【Web】全角約物カッコや半角文字列のアキ調整スクリプト

2023年01月15日

はじめに

これまでJavaScriptはあまり弄ってこなかったのですが、そうは言ってもいられなくなってきました。

After Effectsや3ds-Maxでもスクリプト機能は用意されているものの、それほど差し迫った必要性もないので、殆ど利用する機会はありませんが、Webサイトで、ちょっとしたことをコンテンツ全体に反映させるにはスクリプトが使えると便利な場面が多いように思います。

今回作りたかったのは、全角約物カッコ、つまり【】や『』、「」など、左右にアキがある約物をcssで詰めるためのスクリプトです。

「なんで全角約物を詰める必要があるの?」と多くの方は思うでしょうが、DTPデザイナーにとってはお馴染みの作業です。

ただ、Webドキュメントとなると殆どの場合、ブラウザ任せになってしまいます。

それも仕方のない話で、最近のようにスマホやタブレットなど、文字の組み方に拘っても頻繁に解像度が変化する閲覧環境では、レイアウトが固定されないので徒労に終わることが多いのです。

でもまあ全角のカッコ類ぐらいはアキ調整したい、と思って実現する方法を調べた結果、現状はこんな感じのようです。

  • 半角約物専用Webフォントを使う(アキ調整はできない)。
  • cssの font-feature-settings を使う(OpenTypeフォントしか使えない)。
  • 約物個別にスタイルを当てる(ぇー

全角約物のアキ調整スクリプトは、需要はありそうなのに、いくら調べてもピンと来るものが見つかりません。

仕方がないので、1ヶ月ほどかけて、何か作れないものかとJavaScriptを勉強してきました。

全角約物と言っても、全部で20種類ぐらいですし、特殊文字に置き換えられるものを除けば数種類のアキ調整が出来れば必要十分かと思います。

約物個別にスタイルをあててみる

という訳で、まずはこんなスクリプトは如何でしょうか。

  1. // 全角約物アキ詰めJavaScript(pタグ)
  2. const strOpen1 = ''; // アキを詰めたい約物を定義
  3. const newOpen1 = '<span class="(アキ調整のclass名)"></span>'; // 置き換える文字列を定義
  4. document.querySelectorAll('#main .contents h3,#main .contents p').forEach(elm => elm.innerHTML = elm.innerHTML.replace(strOpen1, newOpen1)); // 置き換え処理のループ

簡単に説明しますと、置き換え前後の文字列を定義して、idやclassやtagなどのcssセレクタで置き換えたい約物がある範囲を抽出してから置き換え処理を繰り返す、といった感じですね。もちろん普通の文字列でも指定出来ますし、半角約物も実現できます。

つまり先ほどの「約物個別にスタイルを当てる」処理をスクリプトにやらせた訳です。

上の例では、id(main)とclass(contents)で指定されたブロックのh3タグとpタグを対象にしていますが、同じスタイルを当てるのであれば、複数のタグをまとめて指定することも出来ます。querySelectorAll便利すぎ。

置き換えるスタイル部分はspanタグなので直接style指定も出来ますが、外部cssに書いた方が調整の面でもセキュリティの面でも良いのではないかと思います。

参考までに、metaタグで”Content-Security-Policy”を’self’設定にしている場合は、ここで直接style指定してもブロックされます。

ここでのpタグを例にとると、querySelectorAllは、cssセレクタで指定するmainなどの親要素に含まれたpタグ全てを配列として抽出しますので、このコードではpタグでマークアップされたテキストの最初に出現した”『”をspanタグ付きに置き換えますが、同じpタグ内だと2度目以降の出現は置き換えません。

まあ、1回出現程度の処理なら大抵cssで間に合うんじゃないかとも思いますが。

使い方の一例としては、行末にマッチする”。”を定義してcssのletter-spacingを-0.5em、1行の文字数に0.5emの余裕を作っておくと、最後の文字が句点ごと次行送りにされる禁則処理を回避できます。

同種の約物が複数回出現する場合

前述したとおり、見出しタグやpタグでマークアップされたテキスト内で約物が1種類あたり1回の出現であれば上記のコードで良いのですが、複数回出現する場合はそうもいきません。

その場合はこんなコードになります。

  1. //約物のアキを詰めるScript
  2. const element1 = document.querySelectorAll('#main .contents h3 , #main .contents p'); //要素を取得
  3. const strOpen = /([])|([])/g; //左アキ約物を定義
  4. const strClose = /([])|([])/g; //右アキ約物を定義
  5. element1.forEach(function(opnStr){ //左アキを詰める
  6. opnStr.innerHTML = opnStr.innerHTML.replace(strOpen,
  7. function(){
  8. if(arguments[1]) {return '<span class="(左アキ用クラス名)">' + arguments[1] + '</span>';}
  9. if(arguments[2]) {return '<span class="(左アキ用クラス名)">' + arguments[2] + '</span>';}
  10. });
  11. });
  12. element1.forEach(function(cloStr){ //右アキを詰める
  13. cloStr.innerHTML = cloStr.innerHTML.replace(strClose,
  14. function(){
  15. if(arguments[1]) {return '<span class="(右アキ用クラス名)">' + arguments[1] + '</span>';}
  16. if(arguments[2]) {return '<span class="(右アキ用クラス名)">' + arguments[2] + '</span>';}
  17. });
  18. });

いきなり長くなりましたが、約物の定義は正規表現で扱い、argumentsオブジェクトのターゲットにアキ調整のスタイルを追加するスクリプトです。

定義する約物は|で区切って増やせますし、arguments[n]は定義した約物の数だけ増やせばOK。

以下のサイトを参考にさせていただきました。

このスクリプトのいいところは、処理の流れがわかりやすくてカスタマイズが容易な点にありますが、応用すると半角文字列前後のアキ調整スクリプトも作れます。

奇数個・偶数個半角文字列における前後のアキ調整スクリプト

タグをまず除外して、残ったテキストからターゲットとなる文字列を抽出し、スタイル付きに置換するスクリプトです。

ただの半角文字列なら上記のサイトにあるコードを参考にすればいいのですが、半角文字列が奇数個または偶数個でアキ調整を変えたい場合は、こんな感じのスクリプトになります。

  1. //奇数個・偶数個半角文字列のアキ調整Script
  2. const value1 = document.querySelectorAll('#main .contents p'); //要素を取得
  3. const odd1 = /(<\/?[^>]+>)|((?<![\s!-;=?-~])[\s!-;=?-~](?:[\s!-;=?-~][\s!-;=?-~])*(?![\s!-;=?-~]))/g; //タグ|奇数個半角文字列を定義
  4. const even1 = /(<\/?[^>]+>)|((?<![\s!-;=?-~])(?:[\s!-;=?-~][\s!-;=?-~])+(?![\s!-;=?-~]))/g; //タグ|偶数個半角文字列を定義
  5. value1.forEach(function(oddStr){ //奇数個のアキ調整
  6. oddStr.innerHTML = oddStr.innerHTML.replace(odd1,
  7. function(){
  8. if(arguments[1]) {return arguments[1];}
  9. if(arguments[2]) {return '<span class="(アキ調整クラス名)">' + arguments[2] + '</span>';}
  10. });
  11. });
  12. value1.forEach(function(evenStr){ //偶数個のアキ調整
  13. evenStr.innerHTML = evenStr.innerHTML.replace(even1,
  14. function(){
  15. if(arguments[1]) {return arguments[1];}
  16. if(arguments[2]) {return '<span class="(アキ調整クラス名)">' + arguments[2] + '</span>';}
  17. });
  18. });

説明を分かりやすくするために奇数個と偶数個でforEach(ループ)処理を分けていますが、後で調整する時にもこの方がやりやすいと思います。

このコードで重要なのは、奇数個・偶数個半角文字列の正規表現でしょう。

正規表現は難しいと考えている方は多いように思いますし、自分も実際にやってみて実感しましたが、意図したとおりにマッチさせるのはとても難しいですね。

以下のサイトには随分とお世話になりました。

最後に

一応の注意点としては、Wordpressなんかでよくある画像の配置制御にpタグやliタグを使っている場合など、要素の取得を定義するときに意図しない場所へspanタグが追加されてしまわないよう気をつけないと、思わぬところが崩れたりします。

こちらとしても、全てのタグ構造に対応できているとは思っていませんので、ご自身のサイトに合わせてカスタマイズしてください。

とまあ、その気になれば文字個別のアキ調整や、レスポンシブに変化する一行の文字数をカウントしてアキ量を変える、なんてことも可能ですが、最初に書いたとおり、レイアウトが固定されない以上、拘りすぎてもそれほど意味がありませんし、調子に乗って個別タグを増やしすぎるとページの読み込みが遅くなったり、XSS脆弱性とかに繋がったりしかねないので、ほどほどにしておきましょう。

約物や半角文字列のアキ調整にスクリプトを使ってみたい方の参考になれば幸いです。

※スクリプトの使用は自由ですが、実装したことによる損害等は、一切責任を負いかねますので、ご了承願います。