77

I have tried to change the images on my site from img to svg, changing img tags to embed and object tags. But, implementing the onclick function, which previously was contained in the img tag, is proving most difficult.

I found onclick had no effect when placed inside the object or embed tag.

So, I made a div exclusively for the svg, and placed onclick in this div tag. But, no effect unless visitor clicks on the edges/padding of the image.

I have read about overlaying a div, but am trying to avoid using absolute positioning, or specifying position at all.

Is there another way to apply onclick to a svg?

Has anyone encountered this problem? Questions and suggestions are welcome.

Rui Jarimba
  • 11,166
  • 11
  • 56
  • 86
user256410
  • 945
  • 3
  • 9
  • 12

14 Answers14

77

You can have an onclick event in the svg itself, I do this all the time in my work. make a rect over the space of your svg, (so define it last, remember svg uses the painters model)

rect.btn {
  stroke:#fff;
  fill:#fff;
  fill-opacity:0;
  stroke-opacity:0;
}

then as an attribute to the rect add the onclick (this can be done with js or jquery as well).

<div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <g>
    <circle ... //your img svg
    <rect class="btn" x="0" y="0" width="10" height="10" onclick="alert('click!')" />
  </g>
</svg>
</div>

this will work in almost all browsers: http://caniuse.com/svg

tomByrer
  • 1,105
  • 12
  • 21
raddrick
  • 4,274
  • 2
  • 27
  • 33
  • Yes this worked for me just wanted to add some comments/clarifications. Please see answer submitted. – codepuppy Oct 24 '12 at 18:08
  • Nice solution. For the CSS it seems like all that's necessary is `fill-opacity: 0`. Why do you set `stroke`, `fill` and `stroke-opacity`? – evanrmurphy Nov 10 '15 at 01:52
  • 1
    they're there just to be explicit, to prevent any other styles from intruding. – raddrick Nov 18 '15 at 19:44
  • This answer's code is not relevant, because the original question is about using svg with embed or object tags. In any case it will function but should not be recommended because it makes no sense to put UI code inside image files. – DAG Aug 16 '16 at 16:47
  • 1
    @DAG the op doesn't specify that he wants to use object or embed, merely mentions them as things he has tried. – raddrick Aug 17 '16 at 00:42
  • Does this work for svgs in object tags, or only with inline svgs? – Sam Sabin Jun 13 '23 at 00:53
15

If you just use inline svg there is no problem.

<svg id="svg1" xmlns="http://www.w3.org/2000/svg" style="width: 3.5in; height: 1in">
  <circle id="circle1" r="30" cx="34" cy="34" onclick="circle1.style.fill='yellow';"
            style="fill: red; stroke: blue; stroke-width: 2"/>
  </svg>
  
peter
  • 41,770
  • 5
  • 64
  • 108
8

This started as a comment on RGB's solution but I could not fit it in so have converted it to an answer. The inspiration for which is entirely RGB's.

RGB's solution worked for me. However, I wished to note a couple of points which may help others arriving at this post (like me) who are not that familiar which SVG and who may very well have generated their SVG file from a graphics package (as I had).

So to apply RGB's solutions I used:

The CSS

 <style>
    rect.btn {
        stroke:#fff;
        fill:#fff;
        fill-opacity:0;
        stroke-opacity:0;
    }
</style>

The jquery script

<script type="text/javascript" src="../_public/_jquery/jquery-1.7.1.js"></script>   
<script type="text/javascript">
   $("document").ready(function(){
       $(".btn").bind("click", function(event){alert("clicked svg")});
   });
</script>

The HTML to code the inclusion of your pre-existing SVG file in the group tag inside the SVG code.

<div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <g>
     <image x="0" y="0" width="10" height="10"
     xlink:href="../_public/_icons/booked.svg" width="10px"/>
    <rect class="btn" x="0" y="0" width="10" height="10"/>

  </g>
</svg>
</div>

However, in my case I have several SVG icons which I wish to be clickable and incorporating each of these into the SVG tag was starting to become cumbersome.

So as an alternative approach where I could employ Classes I used jquery.svg. This is probably a shameful application of this plugin which can do all sorts of stuff with SVG's. But it worked using the following code:

<script type="text/javascript" src="../_public/_jquery/jquery-1.7.1.js"></script>       
<script type="text/javascript" src="jquery.svg.min.js"></script>
<script type="text/javascript">
    $("document").ready(function(){
        $(".svgload").bind("click", function(event){alert("clicked svg")});

         for (var i=0; i < 99; i++) {
           $(".svgload:eq(" + i + ")").svg({
              onLoad: function(){
              var svg = $(".svgload:eq(" + i + ")").svg('get');
              svg.load("../_public/_icons/booked.svg", {addTo: true,  changeSize: false});        
              },
              settings: {}}
          ); 
        } 
    });
</script>

where HTML

<div class="svgload" style="width: 10px; height: 10px;"></div>

The advantage to my thinking is that I can use the appropriate class where ever the icons are needed and avoid quite a lot of code in the body of the HTML which aids readability. And I only need to incorporate the pre-existing SVG file once.

Edit: Here is a neater version of the script courtesy of Keith Wood: using .svg's load URL setting.

<script type="text/javascript" src="../_public/_jquery/jquery-1.7.1.js"></script>       
<script type="text/javascript" src="jquery.svg.min.js"></script>
<script type="text/javascript">
    $("document").ready(function(){

      $('.svgload').on('click', function() {
          alert('clicked svg new');
      }).svg({loadURL: '../_public/_icons/booked.svg'});

    });
</script>
Manikandan C
  • 668
  • 1
  • 9
  • 22
codepuppy
  • 1,130
  • 2
  • 15
  • 25
7

In case you're fine with wrapping the svg in another element (a for example) and putting onclick on the wrapper, svg {pointer-events: none;} CSS will do the trick.

iairu
  • 179
  • 1
  • 2
5

I got this working accross the latest versions of Firefox, Chrome, Safari and Opera.

It relies on a transparent div before the object that has absolute position and set width and height so it covers the object tag below.

Here it is, I've been a bit lazy and used inline styes:

<div id="toolbar" style="width: 600px; height: 100px; position: absolute; z-index: 1;"></div>
<object data="interface.svg" width="600" height="100" type="image/svg+xml">
</object>

I used the following JavaScript to hook up an event to it:

<script type="text/javascript">
    var toolbar = document.getElementById("toolbar");
    toolbar.onclick = function (e) {
        alert("Hello");
    };
</script>
Richard Garside
  • 87,839
  • 11
  • 80
  • 93
3

It worked by simply replacing the <embed/> tag with <img/> and deleting the type attribute.

For instance, in my code, instead of:

<embed src=\"./images/info_09c.svg\" type=\"image/svg+xml\" width=\"45\" onClick='afiseaza_indicatie($i, \"$indicatii[$i]\")'> 

which does not answer the clicking, I wrote:

<img src=\"./images/info_09c.svg\" height=\"25\" width=\"25\" onClick='afiseaza_indicatie($i, \"$indicatii[$i]\")'> 

It works in Internet Explorer and Google Chrome, and I hope that in the other browsers too.

bfavaretto
  • 71,580
  • 16
  • 111
  • 150
  • I guess you're escaping the quotations for xml or something, but this worked in IE at least, when it didn't work when displaying an svg in an (similar to the embed example). As long as this you're using modern browsers, I don't see why this wouldn't be the easiest way when using html5/js stuff like – whyoz Jun 12 '14 at 21:12
  • 1
    Tried replacing a reference with img src to get onclick to work. Tested working under safari, chrome, and firefox, and chrome on android. Thanks for this solution - it's much simpler and avoids cluttering my svg with inline javascript. – Vivek Gani Apr 25 '16 at 04:10
1

You could use following code:

<style>
    .svgwrapper {
        position: relative;
    }
    .svgwrapper {
        position: absolute;
        z-index: -1;
    }
</style>

<div class="svgwrapper" onClick="function();">
    <object src="blah" />
</div>

b3ng0 wrote similar code but it does not work. z-index of parent must be auto.

1

When embedding same-origin SVGs using <object>, you can access the internal contents using objectElement.contentDocument.rootElement. From there, you can easily attach event handlers (e.g. via onclick, addEventListener(), etc.)

For example:

var object = /* get DOM node for <object> */;
var svg = object.contentDocument.rootElement;
svg.addEventListener('click', function() {
  console.log('hooray!');
});

Note that this is not possible for cross-origin <object> elements unless you also control the <object> origin server and can set CORS headers there. For cross-origin cases without CORS headers, access to contentDocument is blocked.

candu
  • 2,827
  • 23
  • 18
0

Perhaps what you're looking for is the SVG element's pointer-events property, which you can read about at the SVG w3C working group docs.

You can use CSS to set what happens to the SVG element when it is clicked, etc.

Danny Bullis
  • 3,043
  • 2
  • 29
  • 35
0

Have you looked into using the CSS z-index property to make the container dev be "on top" of the svg? Because the div is (presumably) transparent, you will still see the image exactly as before.

This, I believe, is the best-practice, non-hack, intended way of solving your problem. z-index is only useful for elements that have a position property of fixed, relative, or, as you've heard, absolute. However, you don't actually have to move the object.

For example:

<style>
    .svgwrapper {
        position: relative;
        z-index: 1;
    }
</style>
<div class="svgwrapper" onClick="function();">
    <object src="blah" />
</div>

For what it's worth, it would also be a little more elegant and safe to not use onClick at all, but instead to bind the click event using javascript. That's another issue altogether, though.

b3ng0
  • 595
  • 4
  • 6
0

Assuming you don't need cross browser support (which is impossible without a plugin for IE), have you tried using svg as a background image?

Experimental stuff for sure, but thought I would mention it.

Russell Leggett
  • 8,795
  • 3
  • 31
  • 45
0

Click on SVG's <g> element in <object> with click event. Works 100%. Take a look on the nested javascript in <svg>. Don't forget to insert window.parent.location.href= if you want to redirect the parent page.

https://www.tutorialspoint.com/svg/svg_interactivity.htm

Imeksbank
  • 104
  • 5
0

I wrapped the 'svg' tag in 'a' tag and put the onClick event in the 'a' tag

C.Chi
  • 11
0

I had a similar issue: it only seems that the onclick event is not occurring, but it is firing twice

  • the svg tag had an onclick option, like: <svg ... onclick="someJsFunction() ...> it opens a dropdown menu, and had a class option named f.e. class-for-svg
  • the path (included in the svg tag) had no any class option,
  • in the other hand, I had a window.onclick function to close the dropdown when the user clicks on the somthing else, here the dropdown was clossing - when the object's class option matched to the svg's class option
  • so when I clicked on the path portion inside the svg tag - the window.onclick event accurred too, and because of the does not matching(to the class name), the dropdown was clossed imediatelly, and it seems that the onclick event is not happening )
  • in reality it happens twise, one opens, an the second closses the drop down
  • solution: add the same class option to the path tag too )

enjoy please )

Hovanes Mosoyan
  • 760
  • 7
  • 10