Entry_Summaryループ内クラス設定を運営者でも触れるようにする
現在提供中の標準テーマの一覧ページでは、横に並ぶコンテンツの数(2列、3列、4列)は、テンプレートの CSS クラスの設定によって決まる仕様になっています。 この記事では、タイトルの通り、運営側でもこの設定を変更できるようにする方法について考えてみます。
問題点の検証
実際にどのような問題が起こるのかを UTSUWA のテーマ「事業紹介」を例に説明していきます。
納品直後の状態
当初、打ち合わせ時には「事業紹介」については増えないと聞いており、コンテンツとしては 2つなので、col-6 と Class を設定し、横に2枚並ぶようにテンプレートに書かれていました。
事業が追加される事に
これがサイトを運営している途中で、新しい事業が増えて 3つになる事になりました。3つ目のコンテンツは下に表示され隣は空白となります。
3つ並べたいと相談される
同じページ内の「ブログ」コンテンツは横に4枚並べて表示していますし、横に3枚並べた方がいいと思う人も多い事でしょう。
CMS で運用している中で、よくある話ではないかと思います。 詳細ページについては、ある程度レイアウト調整もできるのですが、沢山の情報を管理可能な CMS では一覧ページについて自由度が低くなる傾向があります。
対策を考えてみる
対策としては、その制御を行っているのはテンプレート上に書かれている 1つの Class という事になるので、それを管理画面側から変更できるようにする事になります。
実は準備済みでした
実は Ver. 2.5.1(2015年8月) から、モジュールの設定に ループ内クラス という設定が追加されています。これを活用する事で、管理画面上から変更できるようになります。 テンプレートに直接書かれている Class を {entry:loop.class} という変数に置き換えるだけです。
運用担当者に分かるのか?
次の問題は、この記述内容が運用担当者に理解できるのかというところになります。
- acms-col-6 acms-col-sm-4 acms-col-md-3
標準テーマの一覧ページでは、コンテンツの横幅を指定するために acms-col-* というクラスを使用します。 このクラスの数字は 12分割のグリッドシステム に基づいており、1行を12としたときに、各コンテンツが何列分の幅を占めるかを決定します。
具体的な例
- acms-col-6 → 12列中6列分を使用(2列表示)
- acms-col-sm-4 → 画面幅が smサイズ以上のとき、12列中4列分を使用(3列表示)
- acms-col-md-3 → 画面幅が mdサイズ以上のとき、12列中3列分を使用(4列表示)
つまり、画面のサイズによって自動的に 2列 → 3列 → 4列 とレイアウトが変化します。
クラス名 | デフォルトのブレイクポイント |
---|---|
acms-col- | 全て |
acms-col-sm- | 480px以上〜 |
acms-col-md- | 768px以上〜 |
acms-col-lg- | 1024px以上〜 |
acms-col-xl- | 1440px以上〜 |
この設定では、制作者には理解できたとしても、サイトを運用している担当者には難しいと感じるのではないでしょうか。
実際の実装
モジュールフィールドに、各画面の幅の時のカラム数のカスタムフィールドを作る事で実装が可能になります。
さらに工夫を
カスタム設定で設定した SELECT( col-base , col-sm , col-md )を元に、表示設定の「ループ内クラス」{entry:loop.class} を自動で書くことができる JavaScript を用意しました。こうすることでテンプレートは標準的な記述のままで済ますことができ、独自に設定したい時にも対応が可能となります。
モジュールフィールドの設定
- <select name="col-base" class="acms-admin-form-width-mini">
- <option value="acms-col-12" {col-base:selected#acms-col-12}>1カラム</option>
- <option value="acms-col-6" {col-base:selected#acms-col-6}>2カラム</option>
- <option value="acms-col-4" {col-base:selected#acms-col-4}>3カラム</option>
- <option value="acms-col-3" {col-base:selected#acms-col-3}>4カラム</option>
- </select>
- <input type="hidden" name="field[]" value="col-base" />
- <select name="col-sm" class="acms-admin-form-width-mini">
- <option value="" {col-sm:selected#}>未設定 (480px以上)</option>
- <option value="acms-col-sm-12" {col-sm:selected#acms-col-sm-12}>1カラム</option>
- <option value="acms-col-sm-6" {col-sm:selected#acms-col-sm-6}>2カラム</option>
- <option value="acms-col-sm-4" {col-sm:selected#acms-col-sm-4}>3カラム</option>
- <option value="acms-col-sm-3" {col-sm:selected#acms-col-sm-3}>4カラム</option>
- </select>
- <input type="hidden" name="field[]" value="col-sm" />
- <select name="col-md" class="acms-admin-form-width-mini">
- <option value="" {col-md:selected#}>未設定 (768px以上)</option>
- <option value="acms-col-md-12" {col-md:selected#acms-col-md-12}>1カラム</option>
- <option value="acms-col-md-6" {col-md:selected#acms-col-md-6}>2カラム</option>
- <option value="acms-col-md-4" {col-md:selected#acms-col-md-4}>3カラム</option>
- <option value="acms-col-md-3" {col-md:selected#acms-col-md-3}>4カラム</option>
- </select>
- <input type="hidden" name="field[]" value="col-md" />
- <span id="col-etc"></span>
今回の JavaScript
最初に INPUT name="entry_summary_loop_class" の値からカスタム設定の SELECT を調整します。その後、SELECT を修正すると INPUT のテキストを書き換え、INPUT 側をダイレクトに修正すると SELECT 側を書き換えるようにしています。
SELECT の OPTION に無い acms-col-2 などが INPUT に書かれた際には、自動的に SELECT に追加し、acms-col- , acms-col-sm- , acms-col-md- でない Class を書いた時には <span id="col-etc"></span> に表示するようにして追加で Class を書いていることも分かるようにしておきました。
- <script>
- ACMS.addListener("acmsReady", function() {
- const loopClass = document.querySelector('input[name="entry_summary_loop_class"]');
- const colBase = document.querySelector('select[name="col-base"]');
- const colSm = document.querySelector('select[name="col-sm"]');
- const colMd = document.querySelector('select[name="col-md"]');
- const colEtc = document.getElementById("col-etc");
- function updateLoopClass() {
- const values = [colBase.value, colSm.value, colMd.value].filter(value => value !== "");
- loopClass.value = values.join(" ");
- }
- function ensureOptionExists(select, value) {
- if (!value || select.querySelector(`option[value="${value}"]`)) return;
- const existingValues = new Set(Array.from(select.options).map(opt => opt.value));
- if (!existingValues.has(value)) {
- const option = document.createElement("option");
- option.value = value;
- option.textContent = value;
- option.setAttribute("data-custom", "true");
- select.appendChild(option);
- }
- }
- function updateSelectValues() {
- const values = loopClass.value.split(" ").map(val => val.trim()).filter(val => val !== "");
- let baseValue = "", smValue = "", mdValue = "";
- let extraValues = [];
- values.forEach(val => {
- if (/^acms-col-sm-\d+$/.test(val)) {
- smValue = val;
- } else if (/^acms-col-md-\d+$/.test(val)) {
- mdValue = val;
- } else if (/^acms-col-\d+$/.test(val)) {
- baseValue = val;
- } else {
- extraValues.push(val);
- }
- });
- ensureOptionExists(colBase, baseValue);
- ensureOptionExists(colSm, smValue);
- ensureOptionExists(colMd, mdValue);
- colBase.value = baseValue;
- colSm.value = smValue;
- colMd.value = mdValue;
- colEtc.textContent = extraValues.length > 0 ? extraValues.join(", ") : "";
- }
- colBase.addEventListener("change", updateLoopClass);
- colSm.addEventListener("change", updateLoopClass);
- colMd.addEventListener("change", updateLoopClass);
- loopClass.addEventListener("input", updateSelectValues);
- updateSelectValues();
- });
- </script>
a-blog cms 独自の実装ポイント
通常の管理画面のモジュールID の設定でテストを行っていた際には
- document.addEventListener("DOMContentLoaded", function () {
- ...
- });
のように記述して動作していましたが、モジュールIDの管理画面は表の表示画面の「編集」リンクからモーダル表示が可能です。その際には既に DOMコンテンツがロード済みという事になり、ここで書かれた JavaScript を動作させることができません。
そこで a-blog cms の方で用意されている ACMS.addListener("acmsReady" ... を利用します。詳しくは「組み込みJSのイベントハンドラ」をご覧ください。
- ACMS.addListener("acmsReady", function() {
- ...
- });
追加のカスタマイズを考える
今回の JavaScript には input[name="entry_summary_loop_class" と Entry_Summary のコンフィグのフィールドが書かれていますが、他のモジュールでも使う事を考慮するような実装も考えられます。
このカスタム設定の SELECT 部分 + JavaScript のファイルを別ファイルにし target_input を変数化します。
- @include("/admin/parts/loop-class.html", {"target_input": "entry_summary_loop_class"})
/admin/parts/loop-class.html の中の JavaScript 部分を
- const loopClass = document.querySelector('input[name="{{target_input}}"]');
のように指定する事で、他のモジュールでも同様に扱いたいことがあれば共用して利用することができるようにできます。
最後に
次のバージョン Ver. 3.2 で標準提供するテーマには {entry:loop.class} を活用したテーマにするようにしようと考えています。
また private/config.system.default.yaml に
- entry_summary_loop_class : acms-col-6 acms-col-sm-4
のような設定を追加を検討します。どのような初期値がいいかは検討しないといけませんが、こうする事で標準テーマで表示設定にある項目が機能する事になります。
動作サンプルを掲載しておきます。
この記事は、developer.a-blogcms.jp にて正式に公開しています。
本ブログでは、検索エンジンが正規の公開先を認識できるよう Canonical設定 を適用し、重複コンテンツとしてインデックスされないよう、noindex設定 を行っています。
https://developer.a-blogcms.jp/blog/custom/summary-entry-loop-class.html