タッチデバイスでGoogle Map等の誤操作を防止しつつ操作できるようにする長押しイベントスニペット

タッチデバイスでGoogle Map等の誤操作を防止できて、Google Map等を操作したい場合にのみ、操作できるようにする方法を紹介します。

タッチデバイスでGoogle Mapにイライラ

スマートフォン、タブレットのようなタッチデバイスで画面いっぱいの領域を占めるGoogle Mapがページ内にあると、Google Mapの下にあるコンテンツ領域にタッチスクロールで移動したいのに、Google Map内のタッチスクロール操作に引っ掛かってしまい、思い通りの操作ができない経験をすることはありませんか。

Google MapをAPIで呼び出しているのであれば、制作者側はGoogle Map領域下でのタッチ操作を無効化する設定は可能です。次のようにAPIを呼び出す際のオプションでdraggablefalseに設定することで、Google Map内のタッチスクロール操作に引っ掛かってしまう事態を避けることができます。

var options = {
	...
	scale_control: true, //縮尺コントーラーを有効化
	scroll_wheel: false, //ホイールのスクロール操作を無効化
	draggable: false, //ドラッグ(タッチを含む)操作を無効化
	...
}

しかしながら、draggablefalseに設定してしまうと、ポインティングデバイスにおいてGoogle Mapのドラッグ操作が無効化され、タッチデバイスでも同様にタッチ操作が無効化されるので、マップ内を移動する操作ができなくなります。

以上のようにタッチスクロールで下のコンテンツに移動したいのにスクロールできなくてイライラされる可能性があったり、マップ内を移動したいのに操作できなくてイライラされる可能性があったりと、タッチデバイスとページ内に埋め込んだGoogle Mapとの相性を調節するのは、制作者にとって至難ではないでしょうか。

操作したい場合のみ操作できるように

タッチデバイスとGoogle Map等との相性をどのように調節すれば良いのでしょうか、理想としては、操作したい場合のみ操作できるということが実現できると、タッチデバイスとGoogle Map等との相性の問題を制作者が心配する必要はなくなるかもしれません。

これを実現するにあたって、長押しという操作がひとつのアイデアとして使えるかもしれません。そこで、Google Map等のコンテンツ上で長押し操作があった場合にのみ、Google Map等の操作を受け付けるようにするコード例を紹介します。多くのサイトで用いられているjQueryを使用したコード例にしています。

ソースコード例

<div class="target">
ここにGoogle Mapなどのリソースを埋め込む
API呼び出しの場合のDOMもここに生成すること
</div>
...
/*CSSの例*/
.target{
	margin:0;
	padding-top:50%;
	border:1px solid #ddd;
	overflow:hidden;
	height:0;
	position:relative;
	display:block;
}
.target::before{
	content:"\9577\62BC\3067\64CD\4F5C\304C\3067\304D\307E\3059";
	/*「長押しすると地図が操作できます」と生成*/
	display:block;
	z-index:100;
	color:#000;
	width:100%;
	position:absolute;
	top:50%;
	left:0;
	margin:-0.5em 0 0 0;
	line-height:1;
	text-align:center;
	font-weight:bold;
	text-shadow:1px 1px 0 #fff;
}
.target::after{
	content:"";
	display:block;
	position:absolute;
	top:0;
	left:0;
	width:100%;
	height:100%;
	background:rgba(255,255,255,0.5);
}
.target.release::before,
.target.release::after{
	display:none !important;
}
.target > iframe,
.target > div{
	/* レスポンシブデザイン想定 */
	position:absolute;
	top:0;
	left:0;
	width:100%;
	height:100%;
}
...
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
(function (window, $, undefined) {
//必要なコードはここから
	var _release = null;
	$('.target').on('pointerdown pointermove pointerup touchstart touchmove touchend mouseenter mouseleave',function(e){
		var $self = $(this);
		if(!$self.hasClass('release')){
			if( e.type=='mouseleave' || _release && (e.type.indexOf('touch') != -1 || e.type.indexOf('point') != -1 ) ){
					clearTimeout(_release);
			}
			if( e.type=='mouseenter' || e.type=='touchstart' || e.type=='pointerdown' ){
				_release = setTimeout(function(){
					$self.addClass('release');
				}, 500 );
			}
		}
	});
//ここまで
}(this, jQuery));
</script>

コード解説

Google Map等のコンテンツを覆う要素を用意します。ここでは<div class="target">として登場しています。CSSで、この要素がGoogle Map等のコンテンツを透過された状態で覆うようにして、あわせてわかりやすいように「長押しすると地図が操作できます」等の文章を付与しておきます(サンプルでは::before,::after擬似要素を使用し、contentプロパティにASCII文字列以外を含める場合、ブラウザが文字エンコーディングを誤って文字化けを起こす原因になるため16進表記のUnicodeへエスケープしています。)。

<div class="target release">という状態になると、透過された状態で覆っていた部分がなくなるようにします。

次にスニペットコードについて解説します。

  1. 長押しされたかどうかをタイマーで判定する変数を用意します。長押し操作で結果的に当該コンテンツを操作できるように解放するので、ここでは_releaseという変数名にしています。
  2. Google Map等を含む親要素を対象に、pointerdown pointermove pointerup touchstart touchmove touchend mouseenter mouseleaveのイベントを設定します。mouseentermouseleaveはポインティングデバイス下でも動作するように含めています。
  3. Google Map等を含む親要素がreleaseのクラス名をもっていない場合に、長押し操作を判定するタイマーが動作するようにします。
  4. mouseenter touchstart pointerdownのイベントの場合に、タイマーを設定します。このタイマーは、500ミリ秒経過すると、Google Map等を含む親要素にreleaseのクラス名を付与するようになっています。
    • ポインティングデバイスでは、Google Map等を含む親要素の領域にポインターが500ミリ秒の間あると、Google Map等を含む親要素にreleaseのクラス名を付与します。500ミリ秒経過する前に、Google Map等を含む親要素の領域から出た(mouseleaveイベントが発火した)場合は、タイマーをリセットします。
    • タッチデバイスでは、タイマーが動作してから500ミリ秒の間に何も動きがない場合に限ってのみ、長押しされていたと判定し、Google Map等を含む親要素にreleaseのクラス名を付与します。タイマーが動作してから500ミリ秒の間に変数_releaseにタイマーが設定されている状態で、再度のtouchstart pointerdownも含む何かしらのイベントがあると、タイマーをリセットします。500ミリ秒間じっと押し続けていた場合を長押しと判定するのに、タイマーのリセット部分がポイントで_release && (e.type.indexOf('touch') != -1 || e.type.indexOf('point') != -1 )とてします。

なお、実用に際してのプラスTipsとしては、500ミリ秒と固定していたのをvar _interval = ('ontouchstart' in document) ? 500 : 1000;のようにしてタッチデバイスとその他のデバイスとでタイマーの時間を変えるのも誤操作の防止の調整には良いでしょうし、レスポンシブデザインで使うことを考えるとメディアクエリでタブレット以下のサイズになった場合にのみ、Google Map等のコンテンツを覆ってしまって操作できないようにするのも良いでしょう。これなら、PCなどでは最初から操作が可能です。

.target::before,
.target::after{
	display:none;
}
@media (max-width:800px) {
	.target::before,
	.target::after{
		display:block;
	}
}

動作サンプル

タッチデバイスで操作感をお試しください。ポンティングデバイスにおいては誤操作の防止が目的なのではなく、あくまでもレスポンシブデザイン用にポンティングデバイスでも動作するようにしている以上の意図はないので、ポインターがのるとすぐに操作できるようになるだけです。

Updated / Published