Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can *you* get SVG on mobile browser accept mouse/touch events? I can't

Tags:

html

mobile

svg

I display an HTML, with an embedded SVG. I want it to detect mouse events, but it isn't working on the mobile (Android Jellybean). It works fine for a desktop browser.

Here is a demonstration page: http://artsyenta.org/misc/ss/j.touchtry1.html .

If you drag the mouse over the circles you see a log of mouse entries into the elements named "j_xxx". This works in Firefox and Chrome.

Open your Android tablet (I've also tried this on somebody's iPhone, with the same results). Drag your finger over the circles and you get a touchenter event only now and then. Nothing else shows.

You can see the whole page and code by viewing page source. It isn't long, the longest part is the SVG definition. The important parts are:

$(document).ready(function() {
  makeSomethingHappen("hello");
});
function makeSomethingHappen(svg) {
  placeATop(true);
  $('[class^=j_]')
    .on("mouseover", function(event) { logAction(event, this); })
    .on("mouseout", function(event) { logAction(event, this); })
    .on("touchstart", function(event) { logAction(event, this); })
    .on("touchend", function(event) { logAction(event, this); })
    .on("touchenter", function(event) { logAction(event, this); })
    .on("touchleave", function(event) { logAction(event, this); })
    .on("touchEnter", function(event) { logAction(event, this); })
    .on("touchLeave", function(event) { logAction(event, this); });
}

var cntAct = 0;
function logAction(ev, ele) {
  cntAct++;
  var logSpan = $('#logTrace');
  logSpan.html("" + cntAct + ": " + ev.type + " '" + $(ele).attr("class") + "'<br/>" + logSpan.html());
}

Here is part of the SVG:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     id="jsvg" x="0px" y="0px" width="376.247px" height="364.318px" viewBox="140 110 130 120"
     enable-background="new 0 0 376.247 364.318" xml:space="preserve">
  <g id="Layer_1">
    <path class="j_aa_" opacity="0.75" fill="#FFFFFF" stroke="#0071BC" stroke-width="0.9925" enable-background="new    " d="M224.739,6.55l-6.414,23.957c-10.377-2.785-21.304-2.785-31.671,0L180.232,6.55C194.813,2.63,210.155,2.63,224.739,6.55z"/> 
    [snip]
  </g>
</svg>

Again, I detect mouse events on a desktop browser but no touch or mouse events for a mobile browser. Is there a missing technique, or there something missing with the mobiles? It fails with the iPhone browser, Google Chrome on Jellybean and Firefox mobile.

Thanks in advance, Jerome.

like image 223
Jerome P Mrozak Avatar asked Apr 19 '13 17:04

Jerome P Mrozak


2 Answers

After a lot of research into plain SVG events and RaphaelJS events, I have a workable solution for each. Here is a RaphaelJS solution:

window.onload = function(e) {
    document.getElementById("rsr").addEventListener("mousemove",
        function(event) { 
            logAction(event, this, "m");
        }, false);

    document.getElementById("rsr").addEventListener("touchmove",
        function(event) {
            if(event.preventDefault) event.preventDefault();
            // perhaps event.targetTouches[0]?
            logAction(event.changedTouches[0], this, "t");
        }, false);
};

The code is not airtight, but illustrates the major points.

First, the events must be registered through the addEventHandler() call. Using the RaphaelJS onmousemove(), etc., handlers doesn't work on the tablet.

Second, for touch events you need to dig into the list of touches. My application only cares about a single finger, and so the [0] event of the list is enough. There are a number of lists -- touches, targetTouches, changedTouches -- so choose an appropriate one.

Third, determine if the window needs to bubble the events. I get more sensitivity to touches if I call preventDefault().

I tested this on a Google Nexus, iPad 3 and iPad Mini. Good results.

I also have a solution for plain SVG. It is based on this site: http://my.opera.com/MacDev_ed/blog/2010/02/01/how-to-get-all-svg-elements-intersected-by-a-given-rectangle

The differences for what I use and the Javascript he uses is that, again, for touches the touches list needs accessing. "root" is the svg element ID for this example. "logTrace" is a span that receives comments.

var root = document.getElementById("root");
var evtt = evt.touches[0];

var rpos = root.createSVGRect();
rpos.x = evtt.clientX;
rpos.y = evtt.clientY;
rpos.width = rpos.height = 1;
var list = root.getIntersectionList(rpos, null);
var maxItemId = list.length <= 0 ? "(no match)" : list[list.length - 1].id;

document.getElementById("logTrace").innerHTML = "screen: (" + evtt.clientX + ", " + evtt.clientY + ") ? uu(" + maxItemId + "): (" + uupos.x.toFixed(0) + "," + uupos.y.toFixed(0) + ")";

I've tested this solution on a Nexus and an iPad successfully. However, it behaves badly on an iPad Mini -- why behave differently on two iPad devices?

I also noticed that the "plain svg" solution doesn't seem to detect as accurately as the RaphaelJS version. Near the edges of my SVG elements the detection just isn't very good with the plain svg detection. I'm consistently getting good results for the RaphaelJS use.

OTOH, the RaphaelJS use is sensitive to the SVG having (fill:none). The plain SVG doesn't care if (fill:none) is set in an element. Choose your poison.

like image 80
Jerome P Mrozak Avatar answered Oct 29 '22 20:10

Jerome P Mrozak


I had that problem and it turns out the iPad considers the opacity of an object for its hit-test function, so if you have something with fill:none it won't register an event.

I successfully tested a path with this style:

.st5 {fill:none;fill-opacity:0.01;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72}

and two event handlers placed on the tag containing the path:

  <g ...  onclick="top.load(3201);" ontouchend="top.load(3201);" > ...path here with style .st5 </g>

the load(id) function is stored in an external JS file.

Another catch is that the SVG has to be placed directly inside the HTML dom and not referenced as an <embed .../>, the latter causes security exceptions

like image 22
Sten Petrov Avatar answered Oct 29 '22 19:10

Sten Petrov