在触摸屏上处理悬停事件

15
我设计的一个网站使用菜单栏,当鼠标悬停时显示子菜单。最初的网站没有使用响应式设计,只针对桌面环境。现在我正在使用响应式设计技术来针对移动设备和平板电脑,其中许多设备是基于触摸而非鼠标操作的。
我面临的主要问题(似乎也是许多其他人面临的问题)是基于悬停的导航菜单:它在鼠标环境中运行得很好,但在触摸设备上,没有可靠的方法来触发悬停,使页面难以使用。
目标如下:
1. 当用鼠标悬停菜单时,显示子菜单。 2. 当用鼠标点击菜单时,打开锚点标签的链接。 3. 第一次触摸菜单时,显示子菜单。 4. 第二次触摸菜单时,打开锚点标签的链接。 5. 无缝切换功能,适用于同时具有鼠标设备的平板电脑。
我的团队不愿在基于鼠标的机器上放弃悬停效果。他们喜欢它,并不想让所有设备都变成点击式菜单。我同意这个想法。
在网上查找后,我没有找到任何解决我的问题的单一解决方案。我将其中一些方法结合起来,并开发出了一个在Android上测试良好,可以完全实现所需功能的东西。您能否提出改进意见或看到此方法存在任何问题?
jQuery(document).ready(function() {
    var touched=false;
    jQuery(".nav").on('touchstart', 'li .has_children', function (e) {  touched=true; });
    jQuery("html").on('mousemove', function (e) { touched=false; });

    jQuery("html").on('click', updatePreviousTouched );

    jQuery(".nav").on('click', 'li .has_children', function (e) {
        updatePreviousTouched(e);
        if( touched ) { 
            if (jQuery(this).data('clicked_once')) {
                jQuery(this).data('clicked_once', false);
                return true;
            } else {
                e.preventDefault();
                jQuery(this).trigger("mouseenter"); 
                jQuery(this).data('clicked_once', true);

            }
        }
        touched=false;
    }); 

    var previous_touched;
    function updatePreviousTouched(e) {
        if( typeof previous_touched != 'undefined' && previous_touched != null && !previous_touched.is( jQuery(e.target) ) ) {
            previous_touched.data('clicked_once', false);
        }
        previous_touched=jQuery(e.target);
    }
}

这里有一个 jfiddle 示例: http://jsfiddle.net/5FfCF/1/

如果您有任何改进的建议,请提出。 如果您发现它对您没有用,请告诉我。 我在这里发布它是为了尝试获得任何改进的建议,并公布这个想法,以便其他人可以受益。

到目前为止,我的测试结果如下:

  1. Windows 7 上的五个主要浏览器:可用。
  2. Android 平板电脑上的默认浏览器:可用。
  3. 带有触摸屏的 Windows 7 笔记本电脑,运行 IE 和 Chrome:有点问题。 该设备运行这两个浏览器的桌面版,因此不会触发“touchstart”事件。 在触摸屏和浏览器之间,某些东西将触摸转换为鼠标事件。 但是,由于鼠标是该设备中不可移动的部分,因此我不认为这是失败。 我希望它可以像平板电脑一样工作,但似乎还没有可用的技术。
  4. iOS iPad:在我编写上述javascript之前,这已经按预期工作了。 看起来iOS在幕后为我们完成一些悬停/单击魔术。 我有一个团队成员正在检查我的javascript是否破坏了iOS功能,我会在得到答案后编辑此帖子。

其他注意事项:

  1. 这仅适用于桌面和平板电脑站点的菜单。 对于手机大小的设备,有一个不同的基于点击的菜单。 我不建议将此解决方案用于手机。

3
此问题似乎不适合在此发布,因为它是一个代码审核请求。更适合在 http://codereview.stackexchange.com 发布。 - John Conde
5
我会尽力为您翻译,原文的意思是“我在那该死的东西里自己陷入了其中。这个网站比毒品还要可怕。” 我将其简化为:“我完全沉浸在这个网站里了,它比毒品还让人上瘾。” - Deryck
1个回答

13

Piddle Diddle Fiddle :)


Fiddle嵌入式演示 (使用手机最方便)


在阅读这段大段文字之前,请先查看演示并确保它是您要寻找的内容。同时,也请在手机上进行测试(我在我的Android上进行了测试,没有遇到任何问题)。

如果您在移动设备与PC设备上有完全不同的代码集,则您无需担心。在iOS设备上适应触摸和悬停事件尤其棘手。但是我通过捕获一系列的鼠标事件来转换到触摸事件成功地解决了这个问题。

对于您的特定情况,由于您希望在这三个设备之间有如此大的区别,我建议运行一个早期脚本来获取屏幕大小,并且如果小于### px x ### px,则将类handheldmobile添加到body中。现在,您只需要将touch捕获代码分配给$('.mobile .nav'),并具有将hover事件分配到PC的单独代码片段。

对于平板电脑,您将不得不决定哪个更重要-使用每个菜单的根菜单项进行点击和导航的能力,还是仅允许在子菜单项上进行单击(这将消除将.mobile类添加到body的需要)。

这里有一些您应该能够在移动设备和桌面设备上使用的代码。

jQuery:

jQuery(document).ready(function () {
    $('.nav').on('click mousedown mouseup touchstart touchmove', 'a.has_children', function () {
        if ( $(this).next('ul').hasClass('open') && !$(this).parents('ul').hasClass('open')) {
            $('.open').removeClass('open');
            return false;
        }
        $('.open').not($(this).parents('ul')).removeClass('open');
        $(this).next('ul').addClass('open');
        return false;
    });
    $(document).on('click', ':not(.has_children, .has_children *)', function() {        
        if( $('.open').length > 0 ) {
            $('.open').removeClass('open');
            return false;
        }
    });
});

通常我不会像我即将要做的那样复制所有 HTML,但在这种特别情况下,您需要按照我这里的方式完全采用它,以避免 inline-block 空格毛刺。比较我的 fiddle 和您的原始文件,看看每个链接悬停/单击有多容易。

<div class="header-nav-menu">
    <ul class="nav">
        <li><a href="http://www.google.com">One</a></li><li>
        <a class="has_children" href="http://www.google.com">Two</a>
            <ul>
                <li><a href="http://www.google.com">2A</a></li><li>
                <a href="http://www.google.com">2B</a>

                </li>
                <li><a href="http://www.google.com">2C</a>

                </li>
            </ul>
        </li><li>
        <a class="has_children" href="http://www.google.com">Three</a>
            <ul>
                <li><a href="http://www.google.com">3A</a>

                </li>
                <li> <a class="has_children" href="http://www.google.com">3B</a>

                    <ul>
                        <li><a href="http://www.google.com">3BI</a>

                        </li>
                        <li><a href="http://www.google.com">3BII</a>

                        </li>
                        <li><a href="http://www.google.com">3BIII</a>

                        </li>
                    </ul>
                </li>
                <li><a href="http://www.google.com">3C</a>

                </li>
            </ul>
        </li><li>
        <a href="http://www.google.com">Four</a></li><li>
        <a href="http://www.google.com">Five</a></li>
    </ul>
</div>

我很确定我没有修改CSS,所以现在将其保留在Fiddle中。希望这可以帮助你迈出下一步。


这个回答看起来很棒,非常感谢。我还没有测试过,但是无论如何我想说非常感谢你的努力。 - Grumblesaurus
我可以尽力帮忙!:D - Deryck
Deryck:看起来他们已经将问题搁置了。我在这里发布了问题:http://codereview.stackexchange.com/questions/38239/handling-hover-events-on-a-touch-screen。你能否重新在那里发布你的答案,以便我可以给它点赞、测试并在它可行时接受它? - Grumblesaurus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接