2

I'm working on creating an interactive map, and at a bit of a road block. What I need to happen is based on what element is clicked in the SVG img, a tooltip window should appear with information about that particular marked spot.

Right now though, I can't even get any of the markers to respond to a click event. Below is the code I am working with. Once the click event is actually responding, I'll be off and running. I'm just stuck as to why this click event is not responding (and getting pretty frustrated as a result).

I've read through this SO post in hopes of understanding why the click responses aren't happening: Include SVG files with HTML, and still be able to apply styles to them? as well as several others with no resolution being applicable to this scenario. The SVG is being inlined properly, the hover event set by the CSS is happening, it's just the click event that is not occurring. Any help will be greatly appreciated!

HTML:

<!DOCTYPE HTML>
 <html>
    <head>
        <title>TITLE</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
        <link rel="stylesheet" href="{$url}/css/style.css">
        <link rel="shortcut icon" href="{$url}/favicon.ico" type="image/x-icon">
        <link rel="icon" href="{$url}/favicon.ico" type="image/x-icon">
    </head>
    <body>
        <img src="{$url}/images/3d-map.svg" id="3d-map" class="svg">

         <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
         <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
         <script src="{$url}/js/scripts.js"></script>
    </body>
</html>

jQuery:

 $(document).ready(function(){
     /*
      * Replace all SVG images with inline SVG
      */

     $('img.svg').each(function(){
     var $img = $(this);
     var imgID = $img.attr('id');
     var imgClass = $img.attr('class');
     var imgURL = $img.attr('src');

     $.get(imgURL, function(data) {
        // Get the SVG tag, ignore the rest
        var $svg = $(data).find('svg');

        // Add replaced image's ID to the new SVG
        if(typeof imgID !== 'undefined') {
            $svg = $svg.attr('id', imgID);
        }
        // Add replaced image's classes to the new SVG
        if(typeof imgClass !== 'undefined') {
            $svg = $svg.attr('class', imgClass+' replaced-svg');
        }

        // Remove any invalid XML tags as per http://validator.w3.org
        $svg = $svg.removeAttr('xmlns:a');

        // Replace image with new SVG
        $img.replaceWith($svg);

     }, 'xml');
 });
});

$(window).load(function(){
    $(".exchange-marker").bind('click', function(e){
        console.log('clicked');
        console.log($(this).attr('id'));
    });
 });

CSS:

.exchange-marker:hover{
    stroke: #fabf23;
    stroke-width: 3;
    transition: all 0.3s;
}

SVG (truncated for brevity):

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1400" height="1200" viewBox="0 0 1400 1200">
    <g id="markers" fill="#2876BC">
        <ellipse id="exchange-1" class="exchange-marker" cx="963.8" cy="853.7" rx="14.9" ry="4.8"/>
        <ellipse id="exchange-2" class="exchange-marker" cx="929.3" cy="833.1" rx="14.9" ry="4.8"/>
    </g>
</svg>
Community
  • 1
  • 1
TomJ
  • 310
  • 1
  • 2
  • 19
  • Hi Tom, usually, if the straight forward obvious approach is not working, sometimes you have to manually add a 'controlled' layer of complexity to it so you know for sure it should work. What I mean is to add layer mask over the entire SVG area. Then use math to determine where the pointer is in relation to the mask on 'top' and the 'svg' underneath. If the math returns true, fire the desired event. As I said, way more bloated and complex, but the end result is guaranteed. If you provide us with sandbox to play in, I can show you what I mean in more detail. – Alexander Dixon Nov 09 '16 at 01:19
  • Thanks Alexander, I believe I understand what you are referring to. Kind of similar to how we used to do image maps back in the HTML 4.01 days, correct? I think this is a good push in the right direction. If I don't get things moving in the next five minutes or so, I'll put up a jsfiddle. Thanks again! – TomJ Nov 09 '16 at 01:23
  • Yes I believe that's what was used back then. Since we're talking about different technologies to solve the task, have you considered the great utility of ``. From the little exposure I've had with, it has proven extremely versatile (recreating full images on the canvas pixel by pixel and mapping every single pixel of an image). There is also the jQuery library mapster which is also a great tool. – Alexander Dixon Nov 09 '16 at 01:29
  • I've worked with canvas a bit, but it's not really appropriate for the overall plan of functionality for this project. I updated my question with the JSFiddle so others can tamper with it a bit. There are some pieces lacking as it is a project for a fairly large client but I need this resolved ASAP – TomJ Nov 09 '16 at 01:41

1 Answers1

0

So the resolution came down to working out timing on this. By simply incorporating a timeout, we were able to get this responding to clicks as it should. And off to the races I go!

jQuery:

$(document).ready(function(){
    initSVGMap();
});

 function initSVGMap(){ 
    var $img = $('img.svg');
    var imgID = $img.attr('id');
    var imgClass = $img.attr('class');
    var imgURL = $img.attr('src');

  $.get(imgURL, function(data) {
    // Get the SVG tag, ignore the rest
    var $svg = $(data).find('svg');

    // Add replaced image's ID to the new SVG
    if(typeof imgID !== 'undefined') {
        $svg = $svg.attr('id', imgID);
    }
    // Add replaced image's classes to the new SVG
    if(typeof imgClass !== 'undefined') {
        $svg = $svg.attr('class', imgClass+' replaced-svg');
    }

    // Remove any invalid XML tags as per http://validator.w3.org
    $svg = $svg.removeAttr('xmlns:a');

    // Replace image with new SVG
    $img.replaceWith($svg);

  }, 'xml');

  setTimeout('initExchangePoints()', 1500); // THIS IS THE FIX
}

function initExchangePoints(){
    $("ellipse").each(function(){
        $(this).bind("click", function(){ console.log("Clicked " + $(this).attr("id")); });
    });
}
TomJ
  • 310
  • 1
  • 2
  • 19
  • Honestly, my first inclination was to suggest you put the event in a setInterval() that scans until it finds it. That method is literally my 'bread and butter' so solving any and all issues with timing. Cheers for having figured it out. – Alexander Dixon Nov 09 '16 at 04:01
  • I don't see why you need the timeout, just call initExchangePoints directly at that point i.e. where you call setTimeout. – Robert Longson Nov 09 '16 at 04:46