This script can be used as a standalone javascript or greasemonkey script. What I am trying to fix is the hover title and on-click's info-window (it should display the address). here is a jsFiddle

// ==UserScript==
// @name        mapMarkers
// @namespace   mapMarkers
// @include     https://www.example.com/*
// @description map markers of addresses in table
// @version     1
// @grant       none
// ==/UserScript==
// find the table and loop through each rows to get the 11th, 12th, 13th cell's content (street address, city and zip respectively
// convert to lat/lon and show markers on map


if (document.getElementById('main_report') !== null) {

    API_js_callback = 'https://maps.googleapis.com/maps/api/js?v=3.exp&callback=initialize';
    var script = document.createElement('script');
    script.src = API_js_callback;
    var head = document.getElementsByTagName("head")[0];
    (head || document.body).appendChild(script);

    var Table_1 = document.getElementById('main_report');


    var DIVmap = document.createElement('div');
        DIVmap.id = 'DIVmap';
        DIVmap.style.border = '2px coral solid';
        DIVmap.style.cursor = 'pointer';
        DIVmap.style.display = '';
        DIVmap.style.height = '35%';
        DIVmap.style.margin = '1';
        DIVmap.style.position = 'fixed';
        DIVmap.style.padding = '1';
        DIVmap.style.right = '1%';
        DIVmap.style.bottom = '1%';
        DIVmap.style.width = '35%';
        DIVmap.style.zIndex = '99';

    var DIVinternal = document.createElement('div');
        DIVinternal.id = 'DIVinternal';
        DIVinternal.style.height = '100%';
        DIVinternal.style.width = '100%';
        DIVinternal.style.zIndex = '999';

        document.body.appendChild(DIVmap);
        DIVmap.appendChild(DIVinternal);

    //Adds a button which allows the user to re-run calcRoute
    var reloadMapButton = document.createElement("button");
    reloadMapButton.setAttribute("type", "button");
    reloadMapButton.setAttribute("href", "#");
    reloadMapButton.textContent="Reload map";
    reloadMapButton.id="calcRoute";
    reloadMapButton.style.zIndex = '1000';
    document.getElementById('Content_Title').appendChild(reloadMapButton);

    window.initialize = setTimeout(function () {
        var myWindow;
        try{
            myWindow = unsafeWindow;
        }catch(e){
            myWindow = window;
        }
        google = myWindow.google;
        directionsService = new google.maps.DirectionsService();
        directionsDisplay = new google.maps.DirectionsRenderer();

        var myLoc = new google.maps.LatLng(28.882193,-81.317936);
        var myOptions = {
            zoom: 13,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            center: myLoc
        }

        map = new google.maps.Map(document.getElementById("DIVinternal"), myOptions);
        var infoWindow1 = new google.maps.InfoWindow();
        //var bounds = new google.maps.LatLngBounds();
        var geocoder = new google.maps.Geocoder();

        function codeAddress(address,i) {
            setTimeout( function () {  // timer to avoid google geocode limits
                geocoder.geocode( { 'address': address}, function(results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    //map.setCenter(results[0].geometry.location);
                    var marker = new google.maps.Marker({
                        map: map,
                        position: results[0].geometry.location
                    });
                } else {
                    alert("Geocode was not successful for the following reason: " + status);
                }
                });
            }, i * 350);
        }

        function calcRoute() {
            if (Table_1.rows.length > 1) { // table has 1 empty row if no search results are returned and first row is always empty
                var newPoint;
                for (var i = 1, row; row = Table_1.rows[i]; i++) {
                    newPoint = codeAddress(row.cells[10].title +  ', ' + row.cells[11].innerHTML + ', ' + row.cells[12].innerHTML, i);
                // bounds.extend(newPoint);
                    marker = new google.maps.Marker({
                        position: newPoint,
                        map: map,
                        title: row.cells[10].title +  ', ' + row.cells[11].innerHTML + ', ' + row.cells[12].innerHTML
                    });
                google.maps.event.addListener(marker, 'click', function () {
                    infoWindow1.setContent(row.cells[10].title +  ', ' + row.cells[11].innerHTML + ', ' + row.cells[12].innerHTML);
                    infoWindow1.open(map, this);
                });
                    // Automatically center the map fitting all markers on the screen
                // map.fitBounds(bounds);
                }
            }
        }
        //reloadMapButton.addEventListener('click', calcRoute);
        document.getElementById("calcRoute").onclick = calcRoute;
        calcRoute();
    }, 1000);
} // if (document.getElementById('main_report') !== null)

sample data

3

There are 3 answers

1
Ian On BEST ANSWER

Answer copied from the Reddit post:

If you carefully think through the code step by step, you can see why the infowindow isn't assigning itself to the markers. Starting from the calcRoute function:

  1. if the table is more than one row
  2. create newPoint variable
  3. for each row in the table starting with the second one:
  4. call the codeAddress function and assign it to newPoint

Let me cut in here. This is where your confusion is starting. codeAddress has no return statement (and even if it did, it would be asynchronous and wouldn't matter [AJAX]), so it isn't actually doing anything to newPoint. The whole marker is being created inside the codeAddress function, rendering the lines below this useless -- there is no position stored in newPoint so no marker is created, and thus no infoWindow is created.

Instead, you have to create the infoWindow inside the geocoding callback, right after you create the marker. You also have to assign that infoWindow to the marker using a click event. Also, if you want the hover title, just add it as a property of the marker called title.

The final code is here: http://pastebin.com/3rpWEnrp

You'll notice I cleaned it up a bit to make it more readable and clear:

  • The document's head can be accessed using document.head -- no need for getting it by the tag name
  • There is a shorthand for assigning two variables the same value: x = y = 3
  • No need for the z-index on the internal div; the external div already has it. (think of this like hold a piece of cardboard over something--anything on the cardboard is also lifted)
  • No need for the unsafeWindow; this is literally unsafe
  • Shorthand for definining multiple variables at once is var x = 3, y = 4;
  • You don't need the codeAddress function because you only call it once, so the whole contents of this just gets put into the calcRoute function.
  • It's less intrusive to use the console instead of alert, unless you really want the user to know, and in that case you need a simpler message
  • Why check if the table is longer than one row when the if statement won't fire in that case because it starts on row 2?
0
mdxe On

Thanks mostly to IanSan5653 on Reddit, I used mostly his code but had to change it a little. Here is working version on jsFiddle and the code below:

// ==UserScript==
// @name        mapMarkers
// @namespace   mapMarkers
// @include     https://www.volusia.realforeclose.com/*
// @description map markers of addresses in table
// @version     1
// @grant       none
// ==/UserScript==

// find the table and loop through each rows to get the 11th, 12th, 13th cell's content (street address, city and zip respectively
// convert to lat/lon and show markers on map

if (document.getElementById('main_report') != null) {

    var script = document.createElement('script');
    script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&callback=initialize';

    (document.head || document.body).appendChild(script);

    var Table_1 = document.getElementById('main_report');

    var DIVmap = document.createElement('div');
        DIVmap.id = 'DIVmap';
        DIVmap.style.border = '2px coral solid';
        DIVmap.style.height = DIVmap.style.width = '35%';
        DIVmap.style.margin = DIVmap.style.padding = '1';
        DIVmap.style.position = 'fixed';
        DIVmap.style.right = DIVmap.style.bottom = '1%';
        DIVmap.style.zIndex = '999';

    var DIVinternal = document.createElement('div');
        DIVinternal.id = 'DIVinternal';
        DIVinternal.style.height = DIVinternal.style.width = '100%';

        document.body.appendChild(DIVmap);
        DIVmap.appendChild(DIVinternal);

    //Adds a button which allows the user to re-run calcRoute
    var reloadMapButton = document.createElement("button");
        reloadMapButton.setAttribute("type", "button");
        reloadMapButton.textContent="Reload map";
        reloadMapButton.id="calcRoute";
        reloadMapButton.style.zIndex = '1000';
        document.getElementById('Content_Title').appendChild(reloadMapButton);
        // reloadMapButton.onclick = calcRoute;

    window.initialize = function () {

        var google = window.google,
            directionsService = new google.maps.DirectionsService(),
            directionsDisplay = new google.maps.DirectionsRenderer(),
            myLoc = new google.maps.LatLng(28.882193,-81.317936),
            myOptions = {
                zoom: 13,
                mapTypeId: google.maps.MapTypeId.ROADMAP,
                center: myLoc
            },
            infoWindow1 = new google.maps.InfoWindow(),
            map = new google.maps.Map(document.getElementById("DIVinternal"), myOptions),
            geocoder = new google.maps.Geocoder();

        function calcRoute() {
            for (var i = 1, row; row = Table_1.rows[i]; i++) {
                console.log("processing row " + i);
                address = row.cells[10].title +  ', ' + row.cells[11].innerHTML + ', ' + row.cells[12].innerHTML,

                setTimeout( function(addr) {  // timer to avoid google geocode limits
                        geocoder.geocode( { 'address': address}, function(results, status) {
                            if (status == google.maps.GeocoderStatus.OK) {
                                var marker = new google.maps.Marker({
                                        map: map,
                                        position: results[0].geometry.location,
                                        title: addr
                                    });
                                google.maps.event.addListener(marker, 'click', function() {
                                    infoWindow1.setContent(addr);
                                    infoWindow1.open(map,this);
                                });
                            } else {
                                console.error("Geocode was not successful for the following reason: " + status);
                            }
                        });
                }(address), i * 400);
            }
        }
        document.getElementById("calcRoute").onclick = calcRoute;
        calcRoute();
    }
}
1
Michael Geary On

Change your calcRoute() function to call another function for each loop iteration. This will capture that function's parameters and local variables in a closure, so they will remain valid in the asynchronous event handler nested inside it.

You have a lengthy string expression repeated three times. Pull that out into a common variable so you only have to do it once.

Use var on your local variables. You're missing one on marker.

Be careful of your indentation. It is not consistent in your code: the marker click listener is not indented the same as the rest of the function it is nested inside. This makes it hard to see what is nested inside what.

Do not use this loop style:

for( var i = 1, row;  row = Table_1.rows[i];  i++ ) { ... }

I used to advocate this type of loop, but it turned out to not be such a good idea. In an optimizing JavaScript environment, it is likely to force the array to become de-optimized. That may or may not happen in your particular code, and the array may be short enough that it just doesn't matter, but it's not a good habit these days. You're better off with a conventional loop with an i < xxx.length test.

So putting those tips together, we have this for your calcRoute():

function calcRoute() {
    // table has 1 empty row if no search results are returned,
    // and first row is always empty
    if( Table_1.rows.length < 2 )
        return;

    for( var i = 1;  i < Table_1.rows.length;  i++ ) {
        addMarker( i );
    }
}

function addMarker( i ) {
    var row = Table_1.rows[i];
    var title = 
        row.cells[10].title +  ', ' +
        row.cells[11].innerHTML + ', ' +
        row.cells[12].innerHTML;
    var newPoint = codeAddress( title, i );
    var marker = new google.maps.Marker({
        position: newPoint,
        map: map,
        title: title
    });
    google.maps.event.addListener( marker, 'click', function () {
        infoWindow1.setContent( title );
        infoWindow1.open( map, this );
    });
}

There are other problems here. You're calling this function:

var newPoint = codeAddress( ... );

But codeAddress() does not return a value! Nor could it return any useful value, because the function issues an asynchronous geocoder request. If you want to use the location provided by the geocoder, you will need to call a function from within the geocoder callback. I don't understand what you're trying to well enough to make a more specific suggestion here.