Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

A wee bit out of my depth here, I hope someone can help me resolve this...

I have a Google map which loads data from an array and also populates a sidebar list of the items.

A click on the sidebar item opens the corresponding infowindow as expected o.k.

When I mouseenter a sidebar item it pans to if necessary and highlights the corresponding marker (and closes any infowindow currently open ).

(.locs is the sidebar container, .loc is an item)

    $(document).on("mouseenter",".loc",function() {
    var thisloc = $(this).data("locid");
    for(var i=0; i<markers.length; i++) {
        if(markers[i].locid == thisloc) {  //get the latlong
            if(lastinfowindow instanceof google.maps.InfoWindow) lastinfowindow.close();
            map.panTo(markers[i].getPosition());
            markers[i].setIcon('../mapIcons/mini-white.png');
        }
    }
});

This bit works fine as I'm just setting a fixed image. However, when I mouseleave the marker fails to revert back to its original state. If I use the setIcon for a specic image it works fine, but I want to refer back to the original icon from the dataset item.

markers[i].setIcon("currmark");

currmark is the var I used to hold the original icon.

$(document).on("mouseleave",".loc",function() {
    var thisloc = $(this).data("locid");
    var currmark = getIcon("mapData.type");
    for(var i=0; i<markers.length; i++) {
        if(markers[i].locid == thisloc) {  //get the latlong
            map.panTo(markers[i].getPosition());
            markers[i].setIcon("currmark");
        }
    }
});

An alert set on 'currmark', the var I used to hold the original icon, shows only the last icon set by the mouseenter setIcon. This seems to idicate to me that the original icon from the dataset (mapdata.type) has been forgotten / replaced by the last setting applied to markers[i]. I think I need to reaccess the dataset to remind the var of the original state when I set

    var currmark = getIcon("mapData.type");

Some added info....

I'm using a utility function that translates a given 'type' in the dataset to an icon, hence the getIcon(type) function.

function getIcon(type) {
switch(type) {
    case "bar": return "../mapIcons/mini-blue.png";
    case "restaurant": return "../mapIcons/mini-red.png";
    case "cafe": return "../mapIcons/mini-yellow.png";
    default: return "../mapIcons/mini-white.png";
}
}

An example of a data entry...

var data = [
{address:'Rua  Calheta',detail:'Restaurant',title:'Dolphin',type:'restaurant',ico:'red',lat:"37.08570947136275",long:"-8.731633722782135"}
];

I hope I've explained this half sensibly. Can anyone assist me, as I was dead chuffed I'd got this far.

2nd Additional Info for Beetroot showing how my marker and sidebar are initially setup....

data.forEach(function(mapData,idx) {

                var marker = new google.maps.Marker({
                    map: map, 
                    position: new google.maps.LatLng(mapData.lat,mapData.long),
                    title: mapData.title,
                    icon: getIcon(mapData.type)
                });
                var link=('<a href="#"' + mapData.title +'">');
                var contentHtml = "<div id='iw' class='iw2'><h3>" + link + mapData.title+"</a></h3>" + mapData.detail+"<br>"+mapData.address+"</div>";
                var infowindow = new google.maps.InfoWindow({
                    content: contentHtml
                });

                google.maps.event.addListener(marker, 'click', function() {
                    if(lastinfowindow instanceof google.maps.InfoWindow) lastinfowindow.close();
                    marker.infowindow.open(map, marker);
                    lastinfowindow = marker.infowindow;                        
                });
                marker.locid = idx+1;
                marker.infowindow = infowindow;
                markers[markers.length] = marker;
                var spot=('<img src="' + (getIcon(mapData.type)) + '" />');

                var sideHtml = '<p class="loc" data-locid="'+marker.locid+'">'+spot+mapData.title+'</p>';
                     $("#locs").append(sideHtml); 

                if(markers.length == data.length) doFilter();

});

EDIT 02/07 The complete source after incorporating Beetroot-Beetroot's slick changes. Here in its entirety as I'm now vary aware that he can only help if he has the whole picture! Now attempting to debug after learning I'm not such a smartass.

<!DOCTYPE html>
<html>
<head>
 <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false& style=feature:all|element:labels|visibility:off">
</script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">
var map;
var markers = [];
var lastinfowindow;
var locIndex;

 //Credit: MDN
if ( !Array.prototype.forEach ) {
  Array.prototype.forEach = function(fn, scope) {
    for(var i = 0, len = this.length; i < len; ++i) {
    fn.call(scope, this[i], i, this);
    }
  }
}

//my data ~ cutdown and local for testing 
var data = [
{address:'Rua Calheta',detail:'Restaurant',title:'Dolphin',type:'restaurant',icotyp:'red',lat:"37.08570947136275",long:"-8.731633722782135"},
{address:'Rua Calheta',detail:'Cafe',title:'Cafe Calheta',type:'cafe',icotyp:'yellow',lat:"37.0858150589029",long:"-8.731550574302673"},
{address:'Rua Calheta',detail:'Bar',title:'Kellys',type:'bar',icotyp:'red',lat:"37.08583217639933",long:"-8.731239438056945"},
{address:'Rua 2nd Abril',detail:'Bar',title:'Godots',type:'bar',icotyp:'blue',lat:"37.08602046860496",long:"-8.731470108032226"}
];

// translate data type: to icon
function getIcon(type) {
 switch(type) {
    case "bar": return "../mapIcons/mini-blue.png";
    case "restaurant": return "../mapIcons/mini-red.png";
    case "cafe": return "../mapIcons/mini-yellow.png";
    default: return "../mapIcons/mini-white.png";
 }
}

function initialize() {
 var latlng = new google.maps.LatLng(37.08597981464561, -8.730670809745788);
 var myOptions = {
    zoom: 18,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
};
 map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);

   // revised beetroot-beetroot 'loop' function to set markers and sidebar at the same time

var $locs = $("#locs");//Used repeatedly inside the loop.

$.each(data, function(idx, mapData) {
//Note: critical assignment of the new marker as a property of `mapData`.
mapData.marker = new google.maps.Marker({
    map: map,
    position: new google.maps.LatLng(mapData.lat,mapData.long),
    title: mapData.title,
    icon: getIcon(mapData.type)
});
var link = '<a href="#"' + mapData.title +'">' + mapData.title + '</a>';
var contentHtml = "<div id='iw' class='iw2'><h3>" + link + "</h3>" + mapData.detail + "<br>" + mapData.address + "</div>";

//What was `marker` must now be referred to as `mapData.marker`
mapData.marker.infowindow = new google.maps.InfoWindow({
    content: contentHtml
});
google.maps.event.addListener(mapData.marker, 'click', function() {
    if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
    //`this` refers back to the clicked element; the appropriate marker
    this.infowindow.open(map, this);
    lastinfowindow = this.infowindow;
});

var spot = '<img src="' + (getIcon(mapData.type)) + '" />';

//Here, it's convenient to create what was `sideitem` as a jQuery object, so we can apply several jquery methods (without making an assignment).
$('<p class="loc" />')
    .html(spot + mapData.title)
    .appendTo($locs)
    .data('mapData', mapData);//<<<<<<<< critical

});

doFilter(); //It should be possible to execute this statement when the loop is finished.

$(document).on("mouseenter", ".loc", function() {
if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
var mapData = $(this).data("mapData");
map.panTo(mapData.marker.getPosition());//<<<< direct reference to the marker avoids the need to loop.
mapData.marker.setIcon(getIcon(''));//get the default icon.
}).on("mouseleave", ".loc", function() {
var mapData = $(this).data("mapData");
mapData.marker.setIcon(getIcon(mapData.type));
}).on("click",".loc",function() {
var mapData = $(this).data("mapData");
mapData.marker.infowindow.open(map, mapData.marker);
 lastinfowindow = mapData.marker.infowindow;
});

/*
Run on every change to any of the checkboxes
*/

 function doFilter() {

if(lastinfowindow && lastinfowindow.close) lastinfowindow.close(); //Close last info windows if still open and new filter applied
     if(!locIndex) {
         locIndex = {};
         //I reverse index markers to figure out the position of loc to marker index
         for(var x=0, len=markers.length; x<len; x++) {
             locIndex[markers[x].locid] = x;
        }
    }

    //what's checked?
    var checked = $("input[type=checkbox]:checked");
    var selTypes = [];
    for(var i=0, len=checked.length; i<len; i++) {
        selTypes.push($(checked[i]).val());
     }
     for(var i=0, len=data.length; i<len; i++) {
        var sideDom = "p.loc[data-locid="+(i+1)+"]";

         //Only hide if length != 0
         if(checked.length !=0 && selTypes.indexOf(data[i].type) < 0) {
             $(sideDom).hide();
            markers[locIndex[i+1]].setVisible(false);
        } else {
             $(sideDom).show();
            markers[locIndex[i+1]].setVisible(true);
        }
    }
}

$(document).on("click", "input[type=checkbox]", doFilter);
}

</script>
<style type="text/css">
#container {width:920px;height:500px; margin-left:auto;margin-right:auto;}

#map_canvas {width: 700px; height: 500px;float:left;border:1px gray solid;margin-right:10px;}

/* sidebar styling */
#locs {margin-top: 0px;padding-top: 0px;margin-left: 5px;height: 500px;width:200px;overflow-y: auto;overflow-x: hidden;}
#locs img {float:left;padding-right:5px;}
.loc {border: 0;width: 200px;padding: 2px;cursor: pointer;margin:0;text-transform:uppercase;font-family:Arial, Helvetica, sans-serif;font-size:0.90em;}

/* checkbox styling */
#form {margin-top: 0px;padding-top: 0px;height: 29px;}
.label {width: 32.5%;margin: 0;padding: 4px 0 4px 4px;display: inline-block;font-family: "Trebuchet MS", Verdana, Arial, sans-serif;text-transform: uppercase;font-size: 0.75em;font-weight: bold;}
.red {color:white;background-color:red;}
.yellow {color:yellow;background-color:black;}
.blue {color:white;background-color:blue;}

/* Map info styling */
#map_canvas div#iw {width:250px;height:65px;overflow:hidden;}
#map_canvas h3 {font-size:1em; margin:0; padding:3px;}
#map_canvas .iw2 a {text-decoration:none;border-bottom:thin blue dashed;}
#map_canvas .iw2 a:hover {text-decoration:none;border-bottom:thin red solid;}
</style>
</head>

<body onLoad="initialize()">
<div id="container">

<div id="map_canvas"></div> <!-- map //-->
<div id="locs"></div> <!-- sidebar //-->

<div id="form" style="margin:0;padding:0;border:thin gray solid;width:910px">
<span class="label blue"><input type="checkbox" name="bars" value="bar">Bars</span>
<span class="label yellow"><input type="

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
121 views
Welcome To Ask or Share your Answers For Others

1 Answer

If, in the loop where the markers are created, you give each sidebar item a reference to its marker instead of the locid, then there's no need to loop to rediscover the marker on mouseenter/mouseleave.

ie, instead of setting :

//My best guess at what the statement must be.
thisloc.data("locid", id);
//Keep this line if thisloc.data("locid") is used elsewhere.

set (within the sidebar item/marker creation loop) :

var marker = ....;
var thisloc = ...;
...
data[i].marker = marker;//add a .marker property to the original dataset.
thisloc.data("itemData", data[i]);//associate the original dataset with the sidebar item

Now the mouseenter/mouseleave handlers should simplify as follows:

$(document).on("mouseenter", ".loc", function() {
    if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
    var data = $(this).data("itemData");
    map.panTo(data.marker.getPosition());//<<<< direct reference to the marker avoids the need to loop.
    data.marker.setIcon(getIcon(''));//get the default icon.
}).on("mouseleave", ".loc", function() {
    var data = $(this).data("itemData");
    data.marker.setIcon(getIcon(data.type));
});

Note: You shouldn't need to pan to the marker again on mouseleave.

This should answer the question posed plus other advantages.

EDIT:

OK, now I've seen the main loop, it can be revised something like this, with various mods all through :

var $locs = $("#locs");//Used repeatedly inside the loop.

$.each(data, function(idx, mapData) {
    //Note: critical assignment of the new marker as a property of `mapData`.
    mapData.marker = new google.maps.Marker({
        map: map,
        position: new google.maps.LatLng(mapData.lat,mapData.long),
        title: mapData.title,
        icon: getIcon(mapData.type)
    });
    var link = '<a href="#"' + mapData.title +'">' + mapData.title + '</a>';
    var contentHtml = "<div id='iw' class='iw2'><h3>" + link + "</h3>" + mapData.detail + "<br>" + mapData.address + "</div>";

    //What was `marker` must now be referred to as `mapData.marker`
    mapData.marker.infowindow = new google.maps.InfoWindow({
        content: contentHtml
    });
    google.maps.event.addListener(mapData.marker, 'click', function() {
        if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
        //`this` refers back to the clicked element; the appropriate marker
        this.infowindow.open(map, this);
        lastinfowindow = this.infowindow;
    });

    var spot = '<img src="' + (getIcon(mapData.type)) + '" />';

    //Here, it's convenient to create what was `sideitem` as a jQuery object, so we can apply several jquery methods (without making an assignment).
    $('<p class="loc" />')
        .html(spot + mapData.title)
        .appendTo($locs)
        .data('mapData', mapData);//<<<<<<<< critical

});
doFilter();//It should be possible to execute this statement when the loop is finished.

And the corresponding mouseenter/mouseleave handlers would be :

$(document).on("mouseenter", ".loc", function() {
    if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
    var mapData = $(this).data("mapData");
    map.panTo(mapData.marker.getPosition());//<<<< direct reference to the marker avoids the need to loop.
    mapData.marker.setIcon(getIcon(''));//get the default icon.
}).on("mouseleave", ".loc", function() {
    var mapData = $(this).data("mapData");
    mapData.marker.setIcon(getIcon(mapData.type));
});

All untested, so may well need debugging.

EDIT2 :

The way I left it above, the array markers wasn't being populated. Now I have seen the function doFilter() I can see that markers should be populated, however it is more convenient for it to be an object rather than an array.

markers = {};//in the same scope as before

At the bottom of the $.each() loop in initialize(), create and populate one array per category, as properties of markers :

if(!markers[mapData.type]) markers[mapData.type] = [];
markers[mapData.type].push(mapData.marker);

With the markers populated in this way, you have a very simple means of identifying all markers in each category, and doFilter() will simplify enormously to :

function doFilter() {
    if(lastinfowindow && lastinfowindow.close) lastinfowindow.close();
    $("input[type=checkbox]").each(function(i, checkbox) {
        var markersArr = markers[checkbox.value];
        if(markersArr) {
            $.each(markersArr, function(i, marker) {
                marker.setVisible(checkbox.checked);
            });
        }
    });
}

EDIT3

To get the sidebar entries to show/hide in sympathy with the category controls, initialize() should be as follows :

function initialize() {
    var latlng = new google.maps.LatLng(37.08597981464561, -8.730670809745788);
    var myOptions = {
        zoom: 18,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

    var $locs = $("#locs");//Used repeatedly inside the loop.

    var marker;
    $.each(data, function(idx, mapData) {
        //Note: critical assignment of the new marker as a property of `mapData`.
        marker = new google.maps.Marker({
            map: map,
            position: new google.maps.LatLng(mapData.lat, mapData.long),
            title: mapData.title,
            icon: getIcon(mapData.type)
        });
        var link = '<a href="#"' + mapData.title +'">' + mapData.title + '</a>';
        var contentHtml = "<div id='iw' class='iw2'><h3>" + link + "</h3>" + mapData.detail + "<br>" + mapData.address + "</div>";

        marker.infowindow = new google.maps.InfoWindow({
            content: contentHtml
        });
        google.maps.event.addListener(marker, 'click', function() {
            closeLastInfoWindow();
            //`this` refers back to the clicked element; the appropriate marker
            this.infowindow.open(map, this);
            lastinfowindow = this.infowindow;
        });

        var spot = '<img src="' + (getIcon(mapData.type)) + '" />';

        //Here, it's convenient to create what was `sideitem` as a jQuery object, so we can apply several jquery methods.
        marker.loc = $('<p class="loc" />')
            .addClass(mapData.type)
            .html(spot + mapData.title)
            .data('marker', {m:marker, type:mapData.type})
            .appendTo($locs).get(0);
        if(!markers[mapData.type]) markers[mapData.type] = [];
            markers[mapData.type].push(marker);
    });

    $(document).on("mouseenter", ".loc", function() {
        closeLastInfoWindow();
        var m = $(this).data("marker").m;
        map.panTo(m.getPosition());//<<<< direct reference to the marker avoids the need to loop.
        m.setIcon(getIcon(''));//get the default icon.
    }).on("mouseleave", ".loc", function() {
        var data = $(this).data("marker");
        data.m.setIcon(getIcon(data.type));
    }).on("click",".loc",function() {
        var m = $(this).data("marker").m;
        m.infowindow.open(map, m);
        lastinfowindow = m.infowindow;
    });
    $("#form").on("click", ".cat", doFilter);

    doFilter();
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...