htmxで browser history API を活用する方法と a-blog cms 実装のポイント

htmx は標準で browser history API をサポートしており、属性に hx-push-url="true" を追加することで、これを簡単に利用できます。a-blog cms をバックエンドで使用する際の注意点をいくつか紹介します。

※(例)と書かれている部分は実際には動作しません。

hx-push-url 属性を設定して正しく動作する GET

記事詳細(例)

<a>タグでの GETリクエストに対しては、htmx のドキュメントに従い、hx-get属性と hx-push-url属性を追加することを推奨します。

<a href="entry-1.html" hx-get="entry-1.html" hx-push-url="true">記事詳細</a>

hx-push-url 属性を設定しても正しく動作しない POST

(例)
この部分に結果が表示されます

POSTリクエストのケースでは、例えば a-blog cms でカスタムフィールド station を選択するための <select> を含むフォームを使用し、結果を表示するテンプレートとして include/htmx/result.html を指定する方法があります。

<form hx-post="" hx-push-url="true" hx-trigger="submit" hx-target="#search-result" hx-ext="ajax-header">
 <select name="station"> 
  <option value="Tokyo">Tokyo</option>
  <option value="Osaka">Osaka</option>
  <option value="Nagoya">Nagoya</option>
 </select>
 <input type="hidden" name="field[]" value="station">
  <input type="hidden" name="tpl" value="include/htmx/result.html">
<input type="submit" name="ACMS_POST_2GET" value="検索" >
</form>
<div id="search-result">この部分に結果が表示されます</div>

a-blog cms の仕様として、<form> の POST 時 name="ACMS_POST_2GET" が送られてくると、検索条件( station = Nagoya )から URL を組み立て、その後リダイレクトされ GET に変換され、以下のような URL でアクセスした結果を取得できます。

https://example.com/htmx/result.html/field/station/Nagoya/

ここで問題が発生します。 上記の URL がブラウザに表示され履歴にも登録されますが、この URL にアクセスすると Ajax で追加される部分的な HTML になってしまいます。この検索結果で履歴に登録されて欲しい URLは、テンプレートファイル include/htmx/result.html のパスが無い以下の URL であって欲しいのです。

https://example.com/field/station/Nagoya/

hx-push-url 属性を設定しても正しく動作する POST 解決編

ここまでで a-blog cms POSTリクエスト後に URL を組み立てる ACMS_POST_2GET の処理が、htmxhx-push-url と相性が悪い状況を解決する方法を考えてみました。

以下に示す JavaScriptコードを追加することで、hx-push-url 属性の動作を維持しつつ、希望する URL形式を browser history API に正しく渡すことができるようになります。

<script>
  addEventListener('htmx:beforeHistoryUpdate', function (event) {
    const proposedUrl = event.detail.history.path;
    const customUrl = proposedUrl.replace(/\/include\/htmx\/.*\.html/, '');
    event.detail.history.path = customUrl;
  });
</script>

処理としては htmx で History を更新する前の path を取得し、/include/htmx/ 〜 .html 部分を削除するという処理を追加しています。

一般的に a-blog cms の部分的なファイルは include ディレクトリに整理して保存するというルールが定着していますので、htmx で読み込むテンプレートファイルは、その include ディレクトリ内に htmx を用意し、その htmx ディレクトリ内で管理というルールでテーマを構築というルールにする事で上記の JavaScript が有効的に動作させることができます。

Unit_List の使用ケースその2:Google MapのAPI 「MarkerClusterer」

エントリー内のユニットを表示するUnit_Listの使用ケース紹介その2では、Google MapのAPI 「MarkerClusterer」を使ったUnit_Listの活用方法をご紹介します。

たとえば、1つの地図に各エントリーに1つずつ挿入されている地図ユニットをピンでまとめたいときに有効です。

MarkerClustererを使うことにより、同じ位置にエントリーが集中していたとしてもピンが多くなりすぎず、すっきりとした見た目になります。


遠くのエントリーは1件表示だけですが、近くの2件はまとめて表示される

3件のエントリーに1つずつ地図が入力されている場合の MarkerClusterer の例


実装方法

/js/makerclusterer.jsと/images/フォルダをGitHubよりダウンロードしてください。

ソースコード:headタグ

headタグ内に以下を記述してください。

<script type="text/javascript" src="https://maps.google.com/maps/api/js?key=%{GOOGLE_API_KEY}&sensor=false"></script> 
<script type="text/javascript" src="/js/markerclusterer.js"></script>

テーマフォルダに/images/フォルダを設置するときは、/js/makerclusterer.jsの以下の記述のパスを変更する必要があります(例: '/theme/site/images/m'; など)。

MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = '../images/m';

ソースコード:MarkerClustererを設置したい場所への記述

HTMLファイル内のMarkerClustererを設置したい場所に以下を記述します。

<div id="map" style="height: 400px;"></div>

ソースコード:地図の基準にしたい緯度経度の情報を登録するカスタムフィールド

ブログのトップに表示させたい場合はブログのカスタムフィールドに、カテゴリーの一覧に表示させたい場合はカテゴリーのカスタムフィールドに記述してください。

※Site2016内の/admin/category/field_realestate.htmlと同じ内容です

<h3 class="acms-admin-admin-title2">一覧ページで表示させる地図の中心の緯度経度設定</h3>
<table class="adminTable acms-admin-table-admin-edit">
<tr>
  <th>地図</th>
  <td class="js-map-editable">
    <div class="acms-admin-form-group">
      <label for="input-text-map-search_text" class="acms-admin-hide-visually">住所で検索する</label>
      <input type="text" name="" value="" id="input-text-map-search_text" class="js-editable_map-search_text" disabled="disabled" size="40" />
      <input type="button" name="" value="検索" class="js-editable_map-search_button acms-admin-btn-admin" disabled="disabled" />
    </div>
    <div class="acms-admin-form-group">
      <img class="js-map_editable-container" src="http://maps.google.com/maps/api/staticmap?center={index_map_lat},{index_map_lng}&zoom={index_map_zoom}&size=640x400&maptype=roadmap&markers={index_map_lat},{index_map_lng}&key=%{GOOGLE_API_KEY}" width="640" height="400" style="display:block;" />
    </div>
  <!-- BEGIN index_map_lat:veil -->
  <label for="input-text-index_map_lat">緯度</label> <input type="text" name="index_map_lat" value="{index_map_lat}" size="9" id="input-text-index_map_lat" class="js-map_editable-lat" />
  <label for="input-text-index_map_lng">経度</label> <input type="text" name="index_map_lng" value="{index_map_lng}" size="10" id="input-text-index_map_lng" class="js-map_editable-lng" />
  <label for="input-text-index_map_zoom">ズーム</label> <input type="text" name="index_map_zoom" value="{index_map_zoom}" size="10" id="input-text-index_map_zoom" class="js-map_editable-zoom" />
  <!-- END index_map_lat:veil -->
  <!-- BEGIN index_map_lat:empty -->
  <label for="input-text-index_map_lat">緯度</label> <input type="text" name="index_map_lat" value="35.172775" size="9" id="input-text-index_map_lat" class="js-map_editable-lat" />
  <label for="input-text-index_map_lng">経度</label> <input type="text" name="index_map_lng" value="136.887466" size="10" id="input-text-index_map_lng" class="js-map_editable-lng" />
  <label for="input-text-index_map_zoom">ズーム</label> <input type="text" name="index_map_zoom" value="7" size="10" id="input-text-index_map_zoom" class="js-map_editable-zoom" />
  <!-- END index_map_lat:empty -->
  <input type="hidden" name="field[]" value="index_map_lat" />
  <input type="hidden" name="field[]" value="index_map_lng" />
  <input type="hidden" name="field[]" value="index_map_zoom" />
  </td>
</tr>
</table>

ソースコード:MarkerClustererを動かすためのJavaScript(ここではHTMLファイルとして使用します。例:map.html)

<script type="text/javascript">
  var map = null;
  var markerclusterer = null;
  var infowindow = new google.maps.InfoWindow();
  var gmarkers = []; 
  // マーカーの作成と、吹き出しの作成
  function createMarker(latlng, info) {
    var marker = new google.maps.Marker({
      position: latlng,
      map: map
    });
    google.maps.event.addListener(marker, 'click', function() {
      infowindow.setContent(info); 
      infowindow.open(map,marker);
    });
    // 情報を保存
    gmarkers.push(marker);
  }
  // クリックイベントを作成する
  function myclick(i) {
    google.maps.event.trigger(gmarkers[i], "click");
  }
  function initialize() {
    // 全体の地図を作成
    <!-- BEGIN_MODULE Category_Field -->
    var myOptions = \{
      zoom: {index_map_zoom},
      center: new google.maps.LatLng({index_map_lat},{index_map_lng}),
      mapTypeControl: true,
      navigationControl: true,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    \}<!-- END_MODULE Category_Field -->
    map = new google.maps.Map(document.getElementById("map"), myOptions);
    google.maps.event.addListener(map, 'click', function() {
      infowindow.close();
    });
    // 場所・吹き出しの情報を定義
    var markers = [
    <!-- BEGIN_MODULE Unit_List id="map" --><!-- BEGIN unit:loop -->
      ["{entry_title}", {unit_field_2},{unit_field_3},"{entry_url}"],<!-- END unit:loop -->[]<!-- END_MODULE Unit_List -->
    ];
    // 吹き出しに入れる要素
    for (var i = 0; i < markers.length; i++) {
      var point = new google.maps.LatLng( markers[i][1],  markers[i][2]);
      var marker = createMarker( point, "<div class='scrollFix'><p class='mapEntryTitle'>" + markers[i][0] + "</p> " + "<p class='mapDetailInfo'>緯度: " + markers[i][1] + "<br> 経度: "  +  markers[i][2] + "</p><p class='mapLinkText'><a href='"+ markers[i][3] +"'>詳しく見る</a></p>" + " </div>") ;
    }
    markers.pop();
    // 地図を表示
    markerCluster = new MarkerClusterer(map, gmarkers);
  }
  window.addEventListener('load', initialize);
</script>

注意点

  • 「MarkerClustererを動かすためのJavaScript」のソースコードは、全てJavaScriptで書かれていますが a-blog cms のモジュールを使っているためHTMLファイル内に記述します
  • 25行目ではCategory_Fieldを使用していますが、もし「地図の基準にしたい緯度経度の情報を登録するためのカスタムフィールド」をカテゴリー以外のカスタムフィールドとして設置した場合、対応するフィールドモジュールに置き換えます

モジュールIDの設定

モジュールID名「map」というUnit_Listのモジュールを作ります。


ユニットタイプの「地図」がチェックされていることを確認してください。

そのほかの設定については、必要に応じて有効にしてください。

SmartPhoto.js を a-blog cms に実装するには


最近のアップルップルの新しい動きとして、a-blog cms に実装する前に JavaScript ライブラリーとして公開して、その後の安定稼働版を取り込んでいくようにしています。ですから、a-blog cms 以外で作られた Webサイトにも利用が可能で、暫定的ではありますが、SmartPhoto.js WordPress 用のプラグイン も準備しています。

2.5.1 / 2.5.1.1 をご利用の方へ アップロード画像がPNG形式に

今回、2.5.1 で実装された新しい機能で、ブラウザ上で画像をリサイズする機能というものがあります。この機能ですが、現状の仕様だとアップロードした画像のほとんどのファイルが形式が png になってしまいます。

もし、現状のバージョンで、png になってしまう事を止めたいという事であれば、 /js/config.js の 510行目のあたりにある resizeImage の設定を off にしてください。

//--------------
// resize image
resizeImage : 'off',

標準が off になっていて、全部 png になってもいいから、大きなファイルでもアップできた方がいい人が on にする仕様にするべきでした。申し訳ありませんでした。

今回の機能としては、canvas を活用してアップロードする前にリサイズ機能をブラウザ側で実装しています。その際に、canvas では png 形式を必須としており一部のブラウザでは、jpeg をサポートしていない事から、このような仕様となっていました。

参考:

今後の改良として、大きな画像というところでは、jpeg である事が多いですので、対応していないブラウザの時のみ png にし、対応しているブラウザであった場合には jpeg になるように、次のバージョン 2.5.1.2 で改良したいと思っています。

近日中にリリース予定ですので、よろしくお願いいたします。

ver2.1.1.4でのモジュールの不具合

ver2.1.1.4をご利用の場合

2015/02/20以前にダウンロードしたver2.1.1.4パッケージで モジュールIDでグローバル利用している場合に正しい表示にならない問題が確認されています。

対応

2015/02/20以前にver2.1.1.4をダウンロードした場合は、 再度パッケージをダウンロードして頂き、/php/ACMS/function.phpを置き換えて下さい。

この度はご迷惑をおかけしてしまい申し訳ございません。 今後ともa-blog cmsをお願い致します。

jQuery2.1.1を使用した場合の不具合

jQuery2系をご利用の場合

a-blog cms ver2.1より jQuery1.11.1, jQuery2.1.1(デフォルト:1.11.1)に対応しましたが、 jQuery2.1.1に切り替えた場合にブラウザの判定周りに問題がある事が分かっています。

現象

IEでの判定周りやテキストユニットの高さを調整する機能がうまく動きません。

対応

次期バージョンで対応予定ですので、それまではjQuery1.11.1をご利用するようにお願い致します。

この度はご迷惑をおかけしてしまい申し訳ございません。 今後ともa-blog cmsをお願い致します。

a-blog cms 2.0.x系のWYSIWYGエディタ(CKEditor)でフル機能を使えるようにする


2.0.x系でCKEditorの機能が少ない問題が発見されました。

以下のバージョンでCKEditorの使えない機能がある事が発見されました。 これは、CKEditorがフルパッケージではなく、スタンダードパッケージが入っている事が原因になります。

使えない機能として、フォントサイズや、フォントカラーなどがあります。

フル機能が使えないバージョン

  • 2.0.1
  • 2.0.0.3
  • 2.0.0.2
  • 2.0.0.1
  • 2.0.0

この問題はver.2.0.1より後に出たバージョンで解決予定です。

CKEditorをフルパッケージに更新する

次のバージョンが出るまで待つのは不便ですので、CKEditorをフルパッケージにする方法をお知らせ致します。

CKEditorのフルパッケージをダウンロード

CKEditor ダウンロードからCKEditorのフルパッケージをダウンロードして下さい。

必須チェックの可変対応方法

お問い合わせフォームなどの入力項目で、チェックボックスの「その他」にチェックをした場合は、その他の内容を必須項目にして、その他にチェックを入れた場合は内容も入れてください。とするケースがあります。 そのやり方を2例紹介します。


必須チェックの可変対応方法

お問い合わせフォームなどの入力項目で、チェックボックスの「その他」にチェックをした場合は、その他の内容を必須項目にして、その他にチェックを入れた場合は内容も入れてください。とするケースがあります。 そのやり方を2例紹介します。