百度地圖覆蓋物點選事件

最近在專案中使用百度地圖的自定義覆蓋物時發現在手機瀏覽器下click無法觸發,雖然有touchstart,touchmove,touchend等方法,但都不適合專案邏輯。

下面的程式碼我簡單地定義了一個紅色矩形div,給它一個click事件,新增到地圖中

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
body, html
{
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
font-family: "微軟雅黑";
}
#allmap
{
margin: 0 0 3px;
height: 100%;
}
.customOverlay
{
background:#ff0000;
width:100px;height:100px;
position:absolute;
}
</style>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=d48dRYg4j4K81puarHtLXBheW40bG1HW"></script>
<title>2種地圖</title>
</head>
<body>
<div id="allmap">
</div>
</body>
</html>
<script type="text/javascript">
/*
*  自定義標註
*/
var CustomOverlay = function (point, options) {
this._point = point;
this.opts = options;
}
// 通過JavaScript的prototype屬性繼承於BMap.Overlay
CustomOverlay.prototype = new BMap.Overlay();
CustomOverlay.prototype.initialize = function (map) {
this._map = map;
var div = this._div = document.createElement("div");
div.className = "customOverlay";
div.style.zIndex = BMap.Overlay.getZIndex(this._point.lat);
div.style.background = "#ff0000";
div.addEventListener("click", function () {
alert(111);
});
map.getPanes().markerPane.appendChild(div);
return div;
}
CustomOverlay.prototype.draw = function () {
var map = this._map;
var pixel = map.pointToOverlayPixel(this._point);
this._div.style.left = pixel.x   "px";
this._div.style.top = pixel.y   "px";
}
//百度地圖API功能
//載入第一張地圖
var map = new BMap.Map("allmap", { mapType: BMAP_NORMAL_MAP });             // 建立Map例項
var point = new BMap.Point(116.404, 39.915);
map.centerAndZoom(point, 11);
map.enableScrollWheelZoom();                  //啟用滾輪放大縮小
var point1 = new BMap.Point(116.414, 39.915);
var c1 = new CustomOverlay(point1);
map.addOverlay(c1);
//    var marker = new BMap.Marker(point);
//    map.addOverlay(marker);
//    marker.addEventListener("click", function () {
//        alert("我是marker,我能觸發click事件");
//    });
</script>

發現問題

用google瀏覽器的的手機模式進行除錯,不會的請按f12

發現在手機模式下click事件並不會觸發。

分析問題

我們禁用地圖的拖拽功能

    map.disableDragging();

發現click事件可以觸發了,為什麼禁用了地圖的拖拽功能就能觸發click事件了呢?

我們可以大膽猜測,是地圖的click事件阻止了覆蓋物click事件的執行。

現在我們可以去驗證一下,按f12找到覆蓋物的元素節點(瀏覽器除錯就不多說了,大家應該都知道)

既然事件被父元素阻止了,那麼按理來說只要把元素新增到其他容器事件應該就能正常被觸發了,我們可以來試試

我把元素拖動到如圖所示的位置,發現果然可以正常地觸發事件了,這表明我的猜測應該是正確的。

知道了問題之後就是要解決問題

我不知道大家有沒有注意到我上面程式碼中註釋掉的5行程式碼,這5行程式碼的作用的新增一個marker,並賦予click事件.

說到marker,他和覆蓋物都是通過同一個函式map.addOverlay新增到地圖中,在網頁中新增的位置也相同,可是為什麼marker的

click事件可以被觸發,而overlay的click事件就不能被觸發呢?

下面我們來看看marker與 overlay有什麼不同

把程式碼新增到專案中,重新整理網頁找到marker的節點

我們發現marker有2個節點,我們首先找到的是上面那個節點,刪除這個節點我們會發現marker還是在地圖上,但是click事件卻

不能被觸發

刪除下面那個節點,marker在地圖上消失了,不過點選marker原來的位置,click還是會被觸發

很明顯百度地圖在新增marker時把marker分為了2部分,一部分為網頁上的顯示,二部分是事件的觸發。

解決問題

雖然沒有分析出百度地圖是如何實現click事件的,不過在研究的過程中也有了解決問題的思路

方法1

手機瀏覽器中最好用的方法還是touchstart,touchend,touchmove,雖然有時候不適用,但是我們可以用這三個事件組成
一個touchClick方法來模擬click事件
下面的程式碼是我從網上找到的方法
//給jquery新增touchClick方法
(function () {
var defaults = {
start: function (self, event) { },
move: function (self, event) { },
end: function (self, event) { }
}
$.fn.touchClick = function (opts) {
if (typeof opts == "function") {
opts = $.extend({}, defaults, { end: opts });
} else {
opts = $.extend({}, defaults, opts);
}
this.each(function () {
var obj = $(this);
obj.bind("touchstart", function (event) {
obj.data("move", false);
opts.start.call(this, event);
}).bind("touchmove", function (event) {
console.log(event.originalEvent.targetTouches.length);
obj.data("move", true);
opts.move.call(this, event);
}).bind("touchend", function (event) {
if (obj.data("move")) {
return;
} else {
opts.end.call(this, event);
}
obj.data("move", false);
});
});
};
})(jQuery);
    $(c1._div).touchClick(function () {
alert("touchClick");
});

這個方法需要jquery框架,還有一個缺點是在小米等手機中靈敏度不是很高

基於以上方法的缺點,我重新找了一個取巧辦法

利用禁止地圖拖拽能夠觸發click事件的特點
        c2._div.addEventListener("touchstart", function () {
map2.disableDragging();  //禁用地圖拖拽功能
});
c2._div.addEventListener("click", function () {
alert("click");
});
c2._div.addEventListener("touchend", function () {
map2.enableDragging();  //啟用地圖拖拽功能
});

上面的程式碼乍一看可行,實際上是錯誤的,這個就要談談事件的執行順序

在手機瀏覽器中滑鼠點選的執行順序是 touchstart->touchmove->touchend->click,這下就應該知道為什麼上面的程式碼
是錯誤的了
我們刪除toouchend事件,給地圖新增touchmove事件
    map.addEventListener("touchmove", function () {
map.enableDragging();
});

這下就能正常執行click事件了

以下是全部的程式碼
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <style type="text/css">
        body, html
        {
            width: 100%;
            height: 100%;
            overflow: hidden;
            margin: 0;
            font-family: "微軟雅黑";
        }
        #map1_container, #map2_container
        {
            width: 100%;
            height: 50%;
            float: left;
            overflow: hidden;
            margin: 0;
        }
        #allmap1
        {
            margin: 0 0 3px;
            height: 100%;
        }
        #allmap2
        {
            margin: 3px 0 0;
            height: 100%;
        }
        .customOverlay
        {
            background: #ff0000;
            width: 100px;
            height: 100px;
            position: absolute;
            cursor: pointer;
            -webkit-tap-highlight-color: rgba(0,0,0,0);
        }
    </style>
    <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=d48dRYg4j4K81puarHtLXBheW40bG1HW"></script>
    <title>2種地圖</title>
    <script src="../../js/jquery.js" type="text/javascript"></script>
</head>
<body>
    <div id="map1_container">
        <div id="allmap1">
        </div>
    </div>
    <div id="map2_container">
        <div id="allmap2">
        </div>
    </div>
</body>
</html>
<script type="text/javascript">
    //給jquery新增touchClick方法
    (function () {
        var defaults = {
            start: function (self, event) { },
            move: function (self, event) { },
            end: function (self, event) { }
        }
        $.fn.touchClick = function (opts) {
            if (typeof opts == "function") {
                opts = $.extend({}, defaults, { end: opts });
            } else {
                opts = $.extend({}, defaults, opts);
            }
            this.each(function () {
                var obj = $(this);
                obj.bind("touchstart", function (event) {
                    obj.data("move", false);
                    opts.start.call(this, event);
                }).bind("touchmove", function (event) {
                    console.log(event.originalEvent.targetTouches.length);
                    obj.data("move", true);
                    opts.move.call(this, event);
                }).bind("touchend", function (event) {
                    if (obj.data("move")) {
                        return;
                    } else {
                        opts.end.call(this, event);
                    }
                    obj.data("move", false);
                });
            });
        };
    })(jQuery);
    /*
    *  自定義標註
    */
    var CustomOverlay = function (point, options) {
        this._point = point;
        this.opts = options;
    }
    // 通過JavaScript的prototype屬性繼承於BMap.Overlay
    CustomOverlay.prototype = new BMap.Overlay();
    CustomOverlay.prototype.initialize = function (map) {
        this._map = map;
        var div = this._div = document.createElement("div");
        div.className = "customOverlay";
        div.style.zIndex = BMap.Overlay.getZIndex(this._point.lat);
        div.style.background = "#ff0000";
        map.getPanes().markerPane.appendChild(div);
        return div;
    }
    CustomOverlay.prototype.draw = function () {
        var map = this._map;
        var pixel = map.pointToOverlayPixel(this._point);
        this._div.style.left = pixel.x   "px";
        this._div.style.top = pixel.y   "px";
    }
    //百度地圖API功能
    //載入第一張地圖
    var map1 = new BMap.Map("allmap1", { mapType: BMAP_NORMAL_MAP });             // 建立Map例項
    var point1 = new BMap.Point(116.404, 39.915);
    map1.centerAndZoom(point1, 11);
    map1.enableScrollWheelZoom();                  //啟用滾輪放大縮小
    var c1 = new CustomOverlay(point1);
    map1.addOverlay(c1);
    $(c1._div).touchClick(function () {
        alert("touchClick");
    });
    var marker = new BMap.Marker(point1);
    map1.addOverlay(marker);
    marker.addEventListener("click", function () {
        alert("我是marker,我能觸發click事件");
    });
    //載入第二張地圖
    var map2 = new BMap.Map("allmap2", { mapType: BMAP_SATELLITE_MAP });            // 建立Map例項
    var point2 = new BMap.Point(116.404, 39.915);
    map2.centerAndZoom(point2, 11);
    map2.enableScrollWheelZoom();                  //啟用滾輪放大縮小
    map2.addEventListener("touchmove", function () {
        map2.enableDragging();
    });
    var c2 = new CustomOverlay(point2);
    map2.addOverlay(c2);
    c2._div.addEventListener("touchstart", function () {
        map2.disableDragging();  //禁用地圖拖拽功能
    });
    c2._div.addEventListener("click", function () {
        alert("click");
    });
</script>