💭

GoogleMapのマーカークリック時にアイコンが変更するようにしました。(スマホ対応注意点あり)

2020/05/20に公開

1. 概要

前回作成した、
GoogleMap のマーカーと、HTML のリストデータの連動表示に加えて、
marker クリック時にアイコンが変更するようにしました。つまり、マーカーと click すると、HTML リストデータのハイライトとマーカーアイコン画像が同時に変化するようになりました。併せて、スマートフォン固有の対応も行いました。デモサイトリンク

アイコンマップ画面

2. コードの説明

2.1. 初期設定

標準で表示されるアイコン画像と、クリックされたときに表示するアイコン画像を定義します。

./map/main.js
const defaultIconUrl = "images/map-icon-gym-default.png";
const currentIconUrl = "images/map-icon-gym.png";
const defaultScaledSize = new google.maps.Size(30, 30);
const currentScaledSize = new google.maps.Size(40, 40);

CreateMakers 関数の中でデフォルトアイコン表示を定義します。この中の icons で定義している部分を、のちほど setIcon で動的に変更していきます。

./map/main.js

  const createMarkers = () => {
    markers
      .getLocation()
      .then((items) => {
        items.forEach((item) => {
 
      // 中略

          const icons = {
            url: defaultIconUrl,
            scaledSize: defaultScaledSize,
          }

      // 省略

}

2.2. イベント処理

マーカーの addListener の中で、クリックされた時の処理を追加します。setIcon メソッドを使用します。
setTimeout を使う理由は以降説明します。

./map/main.js

  // markerをクリックしたときの処理
  google.maps.event.addListener(marker, "click", function (e) {
    // クリック済みのMakerに対応するliリストのCSS背景を初期化
    maplistLi.forEach((item) => {
      if (item.classList.contains("clicked")) {
        item.classList.remove("clicked")
      }
    })
    // clickしたマーカーのアイコンを変更する処理(※1の処理の後)
    setTimeout(function () {
      marker.setIcon({
        url: currentIconUrl,
        scaledSize: currentScaledSize,
      })
    }, 10)

  // 以下省略

2.3. 躓いた点1:アイコンが初期化されない

上記の処理では、クリックしたマーカーのアイコンは問題なく変更されますが、以前にクリックしたアイコンが標準のものにもどらずそのままとなってしまいます。

このバグを解消する為の情報をネットで検索したところ、英語の記事で、Mouseout イベントを使用して対応する方法の例がありましたが、望んだ挙動ではないので採用しませんでした。

問題のある画面

問題のある画面

いろいろ考えた結果、マーカーのイベントリスナーだけでは、難しいとかんがえ、GoogleMapsAPI の dom のリスナーを使うことにしました。

具体的には、google.maps.event.addDomListener をつかって、HTML のリストが click されたら、いったんアイコンをデフォルトに戻す。地図エリア(新しいマーカー)が click されたら、同じく、いったんアイコンをデフォルトに戻す処理を行います。

ただし、マーカーがクリックされた場合は、地図エリアの   google.maps.event.addDomListener の処理とマーカーに元々付与されているイベント処理がかさなり、アイコンがデフォルトのままになってしまいます。
その為、マーカーがクリックされた場合の処理は、setTimeout をつかって、ほんの少しだけ、マーカー側のクリック処理を遅らせます。

つまり、地図エリアのアイコンをデフォルトにすべて戻してから、クリックされたマーカーの画像だけを変更する処理としました。

./map/main.js
// clickしたマーカーのアイコンを変更する処理
setTimeout(function() {
  marker.setIcon({
    url: currentIconUrl,
    scaledSize: currentScaledSize,
  });
}, 10);

2.4. 躓いた点2:PC とスマホの挙動の違い

ところが、さらに問題が発生しました。この方法では PC 上ではうまくいくのですが、スマホの実機画面では、クリックして変更されたマーカーがもとに戻らない状態になりました。

最終的には以下の方法で対応しました。

スマホ用に touchstart のイベントをトリガーにし、PC 用のクリックイベントとは、flag をもちいて場合分けするように設定することで、問題なく動作しました。

./map/main.js
// 新しくマーカーをクリックしたときに、他のアイコンを初期状態にする。
let flag = false;
google.maps.event.addDomListener(mapCanvas, "touchstart", function() {
  flag = true;
  marker.setIcon({
    url: defaultIconUrl,
    scaledSize: icons.scaledSize,
  });
});

google.maps.event.addDomListener(mapCanvas, "click", function() {
  if (flag) {
    flag = false;
  } else {
    marker.setIcon({
      url: defaultIconUrl,
      scaledSize: icons.scaledSize,
    });
  }
});

3. 参考サイト

GoogleMapsJavaScriptAPI Marker setIcon

GoogleMapsJavaScriptAPI Events

JS で touchstart と click イベントを 2 回発生させないようにしつつ同時に指定する

アイコン ICONFINDER

Discussion