clientX not working in iOS

so I’m trying to capture the coordinates of a tap to place a red X on the screen where the user taps.

var xPosition = event.clientX works fine in the browser (either Chrome or Safari) but on an iPad xPos it returns NaN.

I tried changing it to var xPosition = touch.clientX according to MDN docs, but the sky fell on my head. touch is now “undefined”.

Any suggestions for how to make this work on an iPad in iBooks? I’m assuming it has something to do with the fact that event.clientX is a mouse event, not a touch event which is why it works in a browser and not on my device.

so messing around with the MDN touch events docs, I defined XPos and Ypos globally and came up with this

document.getElementById('wrongTapArea').addEventListener('touchstart', function(e) {
  		xPos = e.clientX;
		yPos = e.clientY;
    }, false);

now, at least, I’m not getting NaN all the time anymore. Instead I’m getting zeros when the user taps on the WrongTapArea element. As that’s what I’ve defined them as globally, they’re just not being changed by this code.

Are you using on touch up or down and not mouse click in the actions

Correct, the difference is that one is a MouseEvent and the other is a TouchEvent; WebKit does not include clientX and clientY in TouchEvents.

Depending on your needs, you may instead be able to use pageX and pageY.
Alternatively you could uncheck Use Touch Events from the Document Inspector and then everything will be mouse events instead (but these aren’t as quickly responsive).

so got it working with a couple of glitches though:

// capture touch events in iOS
hypeDocument.getElementById('wrongTapArea').addEventListener('touchstart', function(e) {
   if(e.touches.length == 1){
    var touch = e.touches[0];
		xPos = touch.clientX;
		yPos = touch.clientY;
}
   //place a red cross where the wrong tap occurred
$('#wrongTapArea').append('<img id="redx" src="'+hypeDocument.resourcesFolderURL() + "/redX.png"+'" style="width:20px; height: 25px; position: absolute; left:'+ xPos +'px; top:'+ yPos +'px" />');        
}, false);

The only issues are that (for reasons which may have nothing to do with this code)

  1. the first tap after the widget activates does not place the red cross. Subsequent taps do. (Yes, the widget is set to start automatically in iBooks)
  2. the code that supposed to throw a ‘game over’ alert after three taps but this doesn’t seem to happen all the time. Sometimes, it pops after five taps, sometimes after three, sometimes four…
  3. The crosses aren’t where I’m expecting them to be. They’re to the right and below. I can adjust this manually and presume it has something to do with CSS positioning of the cross.

I’d have to see in context about those problems.

One common pitfall is that if you are using addEventListener On Scene Load and have multiple scenes, then if you go back to that scene (or have this on multiple On Scene Load handlers) then it will accumulate and get called multiple times. You’ll either need to add removeEventListener on scene unload, or make sure it is only added once.

find the errors working copy.hype.zip (133.5 KB)

good point Jonathan. Thanks for that.

I’ve only got one scene at the moment but may add more. I’ve attached the file. Obviously, it’ll only work on a touch device as it’s currently coded. It’s the iBooks context that I’m looking to make this work in.

The issue is that you are adding the event listener in the same lose() handler, so the first time it is invoked it will add the handler, but it won't be triggered that time because it hadn't yet been added. For situations like this, you will want to only add it once, and in the On Scene Load handler.

I'm not sure I saw this one.

The issue here is that you are using clientX/clientY, which correspond to the window location, but you are using those coordinates within in an element that is offset from the corner of the window; hence your amount off is the offset. There's a few ways to solve this, but since you are only targeting iBooks you can instead of using clientX/clientY you can use layerX/layerY. This is a property on the event itself. In fact, you can make this work without the touchstart handler entirely (and thus also work on desktop for easier testing) by just having the lose() code look like:

	// find the location of the touch, Mouse events use offsetX, touch events use layerX
	var xPos = event.offsetX || event.layerX;
	var yPos = event.offsetY || event.layerY;
	
	// add an image at that location, offset for half the image width/height to center
	$('#wrongTapArea').append('<img id="redx" src="'+hypeDocument.resourcesFolderURL() + "/redX.png"+'" style="width:20px; height: 25px; position: absolute; left:'+ (xPos - 10) +'px; top:'+ (yPos - 12) +'px" />');
    
	incorrect++; // increment the number of wrong taps
	
	if(incorrect == 3) { // if this equals 3
		$("#cover, #loseMessage").fadeIn(); // give feedback
	} 

The secondary problem which this code solves is the X position also needs to be centered on the click, which means you need to subtract half the width/height of the image in the position.

2 Likes

Hi Jonathan… we’ve moved on here with our workload so I will get back to this but not until we hit our deadline in the next few weeks. Just wanted to say I appreciate the time and detail you put into your reply and I’ll revisit this when I have time. Thanks!

3 Likes