0

I try to insert a marker with a form in InfoWindow, but with the first click into the map I got this error:

Uncaught TypeError: Cannot set property 'value' of null

It is because getElementById is NULL, when I click a second time it is matching. I tried some hours but didn't get it :-/

My code (part of):

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">

<script src="https://maps.googleapis.com/maps/api/js"></script>
<script>

var map;
var marker;

function initJsMap() {
  var vienna = new google.maps.LatLng(48.2134567, 16.3789012);
  var mapOptions = {
    position: vienna,
    zoom: 12,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  map = new google.maps.Map(document.getElementById("map-canvas"),mapOptions);

  google.maps.event.addListener(map, 'dblclick', function(event) {
    placeMarker(event.latLng);
  });

}

function placeMarker(location) {
  if (marker) {
    marker.setPosition(location);
  } else {
    var form = '<div id="form_canvasform">' +
                '<form method="POST" action="/addmap" name="form_canvas">' +
                '<div style="display:none;">' +
                '<input id="csrf_token" name="csrf_token" type="hidden" value="foo">' +
                '<input id="latitude" name="latitude" type="hidden" value="">' +
                '<input id="longitude" name="longitude" type="hidden" value=""></div>' +
                '<label for="link">link</label> <input id="link" name="link" type="text" value="">' +
                '<input type="submit" value="Go!">' +
                '</form>' +
                '</div>';
    infowindow = new google.maps.InfoWindow({
      content: form
    });
    marker = new google.maps.Marker({
      position: location,
      map: map,
      draggable: true
    });
    infowindow.open(map, marker);
    marker.addListener('click', function() {
      infowindow.open(map, marker);
    });
  }
  console.log("Latitude: " + location.lat());
  console.log("Longitude: " + location.lng());
  console.log(form);
  document.getElementById('latitude').value = location.lat();
  document.getElementById('longitude').value = location.lng();
}

</script>
</head>
<body onload="initJsMap()">
<div id="map-canvas" style="width:50%; height:600px;"></div>
</body>
</html>

The form content is generated by jinja2 templating engine (with flask). With the console.log() I get the correct values.

[EDIT]:

This is the jinja code who generate the form:

    var form = '<div id="form_canvasform">' +
      '<form method="POST" action="{{ request.path }}" name="form_canvas">' +
      '{{ form.hidden_tag() }}' +
      '{{ form.link.label }} {{ form.link }}' +
      '<br/>' +
      '<input type="submit" value="Go!">' +
      '</form>' +
      '</div>';
duncan
  • 31,401
  • 13
  • 78
  • 99
ic14
  • 31
  • 6

2 Answers2

1

It has to do with you trying to access the form's hidden elements before the infoWindow has properly opened.

EDIT: Here's one way of doing it, but it requires some re-schuffling.

I am using String.replace to change the values of the form. I don't know if there is an easier way of doing it using vanilla JavaScript (as it is easier to do it with jQuery), but it seems to work.

var map;
var marker;

// regex to look for in HTML string
var latRe = /(<input id="latitude" name="latitude" type="hidden" value=")(.*)("><input id="longitude")/; 
var lngRe = /(<input id="longitude" name="longitude" type="hidden" value=\")(.*)(">.*<label)/;

function initJsMap () {
  var vienna = new google.maps.LatLng(48.2134567, 16.3789012);
  var mapOptions = {
    center: vienna, // 'center', not 'position'
    zoom: 12,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
  marker = new google.maps.Marker({  map: map, draggable: true });
  infowindow = new google.maps.InfoWindow();

  google.maps.event.addListener(map, 'dblclick', function(event) {
      placeMarker(event.latLng);
  });

  marker.addListener('click', function() {
      infowindow.open(map, marker);
  });
}

function placeMarker (location) {
    var form = 
      '<div id="form_canvasform">' +
      '<form method="POST" action="/addmap" name="form_canvas">' +
      '<div style="display:none;">' +
      '<input id="csrf_token" name="csrf_token" type="hidden" value="foo">' +
      '<input id="latitude" name="latitude" type="hidden" value="">' +
      '<input id="longitude" name="longitude" type="hidden" value=""></div>' +
      '<label for="link">link</label> <input id="link" name="link" type="text" value="">' +
      '<input type="submit" value="Go!">' +
      '</form>' +
      '</div>';
    // ugly: replace the values in the HTML string with the new values using regex
    form = form.replace(latRe, '$1' + location.lat() + '$3');
    form = form.replace(lngRe, '$1' + location.lng() + '$3');
    // set the new form
    infowindow.setContent(form);
    // set the new position of the marker
    marker.setPosition(location);
    // open infowindow
    google.maps.event.trigger(marker, 'click');
}

If you need information about the regular expressions that I used, check this answer.

DEMO

Community
  • 1
  • 1
Mikey
  • 6,728
  • 4
  • 22
  • 45
  • Yeah, would be nice. But as noted, the form is generated by jinja2 and so I have no direct access to `value`. I substituted the jinja2 code for clarity. Thanks for mentioning! – ic14 May 29 '16 at 16:59
  • I've updated my answer. Click on 'Go' in the demo to see the different coordinates in the console. – Mikey May 29 '16 at 18:38
  • Thanks for showing us another possibility. I used the hint from @Dr.Molle because is a bit clearer what the code does. – ic14 May 30 '16 at 13:36
1

When you use a string as content for an InfoWindow the elements inside the InfoWindow are not available before the InfoWindow has been opened(is domready)

But you must not use a string as content, youz may also use a DOMNode directly, this node will always be accessible, no matter if it has already been injected into the document or not.

So the solution: create a DOMNode based on the form-string, and use it as infowindow-content

Demo:(I've modified form a little bit for the demonstration, but it will also work with the original string)

function initJsMap() {
  var vienna      = new google.maps.LatLng(48.2134567, 16.3789012),
      mapOptions  = {
                      center: vienna,
                      zoom: 12,
                      disableDoubleClickZoom:true
                    },
      map         = new google.maps.Map(document.getElementById("map-canvas"),mapOptions),
      marker      = new google.maps.Marker({draggable: true}),
      infowindow  = new google.maps.InfoWindow({disableAutoPan:true}),
      form        = '<div id="form_canvasform">' +
                    '<form method="POST" action="/addmap" name="form_canvas">' +
                    '<input id="latitude" name="latitude"  value=""><br/>' +
                    '<input id="longitude" name="longitude"  value="">' +
                    '</form>' +
                    '</div>';
      node        = document.createElement('div'),
      content;
      
      node.innerHTML  = form;
      content         = node.firstChild
      infowindow.setContent(content);
      
      google.maps.event.addListener(map, 'dblclick', function(event) {
        marker.setOptions({position:event.latLng,map:map});
        infowindow.open(map,marker);
      });
      google.maps.event.addListener(marker, 'click', function(event) {
        marker.setPosition(event.latLng);
        infowindow.open(map,marker);
      });
      
      google.maps.event.addListener(marker, 'position_changed', function() {
        content.querySelector('input[name="latitude"]').value  = this.getPosition().lat();
        content.querySelector('input[name="longitude"]').value = this.getPosition().lng();
        if(infowindow.getMap()){
          infowindow.open(map,marker);
        }
      });
}
html,body,#map-canvas{
  padding:0;
  margin:0;
  height:100%;
}
<div id="map-canvas"></div>
<script src="https://maps.googleapis.com/maps/api/js?v=3&callback=initJsMap" async defer></script>
Dr.Molle
  • 116,463
  • 16
  • 195
  • 201
  • Thanks a lot! Very helpful, learned something new and I like your code styling, it is very readable. Try to use that, too :) – ic14 May 30 '16 at 13:34