最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Android 4 Chrome hit testing issue on touch events after CSS transform - Stack Overflow

programmeradmin3浏览0评论

I'm having issues with the bination of CSS transforms and touch event hit testing. This only reproduces for me in Chrome on Android 4 (stable and beta). iOS Safari, as well as Chrome desktop with touch emulation both appear to be working fine.

I'm almost positive this has to be a bug, so I think I'm mostly looking for workarounds here.

The issue is that hit testing for touch only seems to work for where the element was before the transform, not the final position. You can see an example on my jsfiddle (only on Android 4 Chrome):

jsfiddle: / full screen: /

If you drag the blue box half way down the screen and release it will snap back to the top. Now, if you try dragging from the top half of the page again, no touch will register. The touch events aren't even fired on the element. However, if you attempt to touch the bottom of the element, it works fine. You can then try moving it up from the bottom, and observing that hit testing no longer works on the bottom, but works on the top.

This is how I'm handling the events:

function handleTouch(e) {

    console.log("handle touch")

    e.preventDefault();

    switch(e.type){
        case 'touchstart':
            console.log("touchstart");
            touchOriginY = e.targetTouches[0].screenY;
            break;
        case 'touchmove':
            console.log("touchmove");
            el.innerHTML = e.targetTouches[0].screenY;
            var p = e.targetTouches[0].screenY - touchOriginY;
            el.style[TRANSFORM] = 'translate3d(0,' + p + 'px' + ',0)';
            break;
        case 'touchcancel':
            console.log("touchcancel");
            // Fall through to touchend
        case 'touchend':
            //console.log("touchend");
            //el.style[TRANSITION] = '.4s ease-out';
            el.style[TRANSFORM] = 'translate3d(0,0,0)';
            break;
    }

}

el.addEventListener('touchstart', handleTouch);
el.addEventListener('touchend', handleTouch);
el.addEventListener('touchmove', handleTouch);
el.addEventListener(TRANSITION_END, function(e) {
    console.log("transition end")
    el.style[TRANSITION] = '';
});

I don't have any problems with the transforms in touchmove, as those aren't new touches to be detected anyways.

Any suggestions?

I'm having issues with the bination of CSS transforms and touch event hit testing. This only reproduces for me in Chrome on Android 4 (stable and beta). iOS Safari, as well as Chrome desktop with touch emulation both appear to be working fine.

I'm almost positive this has to be a bug, so I think I'm mostly looking for workarounds here.

The issue is that hit testing for touch only seems to work for where the element was before the transform, not the final position. You can see an example on my jsfiddle (only on Android 4 Chrome):

jsfiddle: http://jsfiddle/LfaQq/ full screen: http://jsfiddle/LfaQq/embedded/result/

If you drag the blue box half way down the screen and release it will snap back to the top. Now, if you try dragging from the top half of the page again, no touch will register. The touch events aren't even fired on the element. However, if you attempt to touch the bottom of the element, it works fine. You can then try moving it up from the bottom, and observing that hit testing no longer works on the bottom, but works on the top.

This is how I'm handling the events:

function handleTouch(e) {

    console.log("handle touch")

    e.preventDefault();

    switch(e.type){
        case 'touchstart':
            console.log("touchstart");
            touchOriginY = e.targetTouches[0].screenY;
            break;
        case 'touchmove':
            console.log("touchmove");
            el.innerHTML = e.targetTouches[0].screenY;
            var p = e.targetTouches[0].screenY - touchOriginY;
            el.style[TRANSFORM] = 'translate3d(0,' + p + 'px' + ',0)';
            break;
        case 'touchcancel':
            console.log("touchcancel");
            // Fall through to touchend
        case 'touchend':
            //console.log("touchend");
            //el.style[TRANSITION] = '.4s ease-out';
            el.style[TRANSFORM] = 'translate3d(0,0,0)';
            break;
    }

}

el.addEventListener('touchstart', handleTouch);
el.addEventListener('touchend', handleTouch);
el.addEventListener('touchmove', handleTouch);
el.addEventListener(TRANSITION_END, function(e) {
    console.log("transition end")
    el.style[TRANSITION] = '';
});

I don't have any problems with the transforms in touchmove, as those aren't new touches to be detected anyways.

Any suggestions?

Share Improve this question asked May 22, 2013 at 23:22 Jason FarnsworthJason Farnsworth 8049 silver badges15 bronze badges 1
  • I think I've somewhat unblocked myself on this for the time being. Removing the innerHTML update from touchmove stops the hit target badness. I hope this doesn't e back to bite me when I legitimately need to update content inside the element later. – Jason Farnsworth Commented May 23, 2013 at 0:24
Add a ment  | 

1 Answer 1

Reset to default 9

This is an unusual bug in Chrome.

Essentially the hit targets for an element is recorded during a layout pass by the browser. Each time you set innerHTML, the browser will relayout and the last time this is done, is before the touchend event is fired. There are a couple of ways around it:

OPTION 1: You can set a touch handler on the body element and check the target of touch event to see if it is touching the red block. Tip of the cap to Paul Lewis for this approach.

http://jsfiddle/FtfR8/5/

var el = document.body;
var redblock = $('.splash-section');

function handleTouch(e) {

    console.log("handle touch")
    if(e.target != redblock) {
        return;
    }

    ....

OPTION 2: Set an empty touch callback on the document seems to fix the problem as well - according to some of the linked bug reports, this causes the hit testing to be done on the main thread which is a hit on performance but it properly calculates the hit targets.

http://jsfiddle/LfaQq/2/

document.body.addEventListener('touchstart', function(){});

OPTION 3: Set innerHTML after the transition has ended to force a relayout:

el.addEventListener(TRANSITION_END, function(e) {
    console.log("trans end - offsettop:" + el.offsetTop);
    el.style[TRANSITION] = '';
    el.innerHTML = 'Relayout like a boss!';
});

I've created a bug report here and Rick Byers has linked to a related bug with additional info: https://code.google./p/chromium/issues/detail?id=253456&thanks=253456&ts=1372075599

发布评论

评论列表(0)

  1. 暂无评论