简体   繁体   中英

Mobile-friendly tooltip not working in Android browser

I'd like to make a tooltip for my website and found a great solution: http://osvaldas.info/elegant-css-and-jquery-tooltip-responsive-mobile-friendly .

They say that it is mobile friendly. And it works great on my ipad, but in my Android (HTC Wildfire if it is necessary) web-browser it doesn't work (it reacts in a way as if it is a simple piece of text).

1) What is the reason, what is special in Androids?
2) How can it be solved and how to make a tooltip mobile friendly for both mobile operating systems?

"1) What is the reason, what is special in Androids?"
FC: A lot, but nothing I can think of that would explain your problem. Not without further specification of your problem, that is.

"2) How can it be solved and how to make a tooltip mobile friendly for both mobile os."
FC: Fortunately, that's easy. With a quite simple native/vanilla Javascript and some easy nested spans: live demo ; used code:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Light-weight Tooltip by FC</title>
<style>
    html {
        font-size: 62.5%;
    }
    body {
        font: normal 1.3em Verdana;
        background-color: white;
        /* just for the JSBin demo */
    }
    h2 {
        text-align: center;
        margin-bottom: 2em;
    }
    span.tool {
        position: relative;
        display: inline-block;
        border-bottom: 1px dotted black;
    }
    span.tool:hover {
        cursor: help;
    }
    span.tip {
        position: absolute;
        bottom: 20px;
        left: 0px;
        display: block;
        width: auto;
        white-space: nowrap;
        font-size: .9em;
        border: 0px solid black; /* change as desired */
        border-radius: 6px;
        padding: 1px 5px;
        background: #eee;
        background: linear-gradient(top, #eee, #ccc);
        background: -moz-linear-gradient(top, #eee, #ccc);
        background: -o-linear-gradient(top, #eee, #ccc);
        background: -webkit-linear-gradient(top, #eee, #ccc);
        background: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#ccc));
        background: -ms-linear-gradient(top, #eee, #ccc);
        filter: progid: DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#eeeeee, EndColorStr=#cccccc);
        zoom: 1;
        visibility: hidden;
    }
</style>
</head>
<body>

<h2>Light-weight Tooltip by FC</h2>

<p>The <span class="tool">WHO<span class="tip">World Health Organization</span></span> was established in 1948.</p>

<p>
    It is part of the
    <span class="tool">UN
        <span class="tip">United Nations, <br>the successor of the <br>League of Nations</span>
    </span>, which was established in 1945.
</p>

<hr>

<p>Explanation and 'minds':</p>

<ul>
    <li>The method consists of local nested spans ('tools' with 'tips' inside), positioned relative-absolute.</li>
    <li>If the same tips are to be used several times throughout the page or website, the tip spans can be populated centrally with Javascript or server-side scripting.</li>
    <li>In the current code the width of the tips is set to <i>auto</i>, and controlled with &lt;br&gt;s in the tip text. Change to fixed width as desired.</li>
    <li>With the current code tablet users must tap (= <i>onclick</i>) rather than press-hold (= <i>onmousedown</i>). It is assumed that that is the intuitive thing most tablet users do, but it can be changed to press-hold.</li>
    <li>The HTML is valid and the code works in IE8 as well.</li>
    <li>For the sake of completeness: IE9 does not form a border-radius when the element has no declared border, or a border-width of 0.</li>
</ul>

<script>
    var tools = document.querySelectorAll('.tool'); // mind the dot
    var nrOfTools = tools.length;
    for (var i = 0; i < nrOfTools; i++) {
        if ('ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch)) {
            tools[i].onclick = function() {
                if (this.children[0].style.visibility == '' || this.children[0].style.visibility == 'hidden')
                    this.children[0].style.visibility = 'visible';
                else
                    this.children[0].style.visibility = 'hidden';
            }
        } else {
            tools[i].onmouseover = function() {
                this.children[0].style.visibility = 'visible';
            }
            tools[i].onmouseout = function() {
                this.children[0].style.visibility = 'hidden';
            }
        }
    }
</script>
</body>
</html>

.
Do note that on dual-mode machines (touchscreen plus keyboard) it seems impossible to detect in which mode it is. So on those machines, one or the other (tap or click) may not work in one of the modes.

Find other imperfections, or do you have more info those machines? Please leave a comment.

I found the solution to this mysterious challenge. Most of the time the limitation that you're having is related to a Dynamic HTML, and the function is always going to return you something like this:

例

This means that the object is not created and jQuery cannot find it then you need to apply something like that is explained here:

https://stackoverflow.com/a/15090957/2889347

The code must be edited and will look like this:

 $(function () {
        $(document.body).on('mouseenter touchstart mouseleave touchend', '[rel=tooltip]', function () {
            var targets = $('[rel=tooltip]'),
                target = false,
                tooltip = false,
                title = false;

            targets.on('mouseenter touchstart', function () {
                target = $(this);
                tip = target.attr('title');
                tooltip = $('<div id="tooltip"></div>');

                if (!tip || tip == '')
                    return false;

                target.removeAttr('title');
                tooltip.css('opacity', 0)
                    .html(tip)
                    .appendTo('body');

                var init_tooltip = function () {
                    if ($(window).width() < tooltip.outerWidth() * 1.5)
                        tooltip.css('max-width', $(window).width() / 2);
                    else
                        tooltip.css('max-width', 340);

                    var pos_left = target.offset().left + (target.outerWidth() / 2) - (tooltip.outerWidth() / 2),
                        pos_top = target.offset().top - tooltip.outerHeight() - 20;

                    if (pos_left < 0) {
                        pos_left = target.offset().left + target.outerWidth() / 2 - 20;
                        tooltip.addClass('left');
                    }
                    else
                        tooltip.removeClass('left');

                    if (pos_left + tooltip.outerWidth() > $(window).width()) {
                        pos_left = target.offset().left - tooltip.outerWidth() + target.outerWidth() / 2 + 20;
                        tooltip.addClass('right');
                    }
                    else
                        tooltip.removeClass('right');

                    if (pos_top < 0) {
                        var pos_top = target.offset().top + target.outerHeight();
                        tooltip.addClass('top');
                    }
                    else
                        tooltip.removeClass('top');

                    tooltip.css({ left: pos_left, top: pos_top })
                        .animate({ top: '+=10', opacity: 1 }, 50);
                };

                init_tooltip();
                $(window).resize(init_tooltip);

                var remove_tooltip = function () {
                    tooltip.animate({ top: '-=10', opacity: 0 }, 50, function () {
                        $(this).remove();
                    });

                    target.attr('title', tip);
                };

                target.on('mouseleave touchend', remove_tooltip);
                tooltip.on('click', remove_tooltip);
            });
        });
    });

The trick is on the:

$(document.body).on('mouseenter touchstart mouseleave touchend', '[rel=tooltip]', function () { //... });

This part of the code it would allow you to call it when you the code is fully loaded on the body and it will track the pattern that you want to execute.

Besides, here are some screenshots of the tooltip working as expected in Android.

图片1 图片2

Also, you can add this additional events:

  • touchstart
  • touchend

UPDATE 2017-07-25:

I had some troubles with certain WebViews and I upgraded the code to the following one, which should be fully functional:

$(function() {
    var target = false,
        tooltip = false,
        title   = false;

    $(document.body).on('click tap', function (e) {
        if (event.target.nodeName !== "ABBR" && tooltip != false) {
            remove_tooltip();
        }
    });

    var remove_tooltip = function()
    {
        tooltip.animate( { top: '-=10', opacity: 0 }, 50, function()
        {
            $( this ).remove();
        });

        target.attr( 'title', tip );
    };

    $(document.body).on('click tap', '[rel=tooltip]', function () {
        target  = $( this );
        tip     = target.attr( 'title' );
        tooltip = $( '<div id="tooltip"></div>' );

        if( !tip || tip == '' )
            return false;

        target.removeAttr( 'title' );
        tooltip.css( 'opacity', 0 )
               .html( tip )
               .appendTo( 'body' );

        var init_tooltip = function()
        {
            if( $( window ).width() < tooltip.outerWidth() * 1.5 )
                tooltip.css( 'max-width', $( window ).width() / 2 );
            else
                tooltip.css( 'max-width', 340 );

            var pos_left = target.offset().left + ( target.outerWidth() / 2 ) - ( tooltip.outerWidth() / 2 ),
                pos_top  = target.offset().top - tooltip.outerHeight() - 20;

            if( pos_left < 0 )
            {
                pos_left = target.offset().left + target.outerWidth() / 2 - 20;
                tooltip.addClass( 'left' );
            }
            else
                tooltip.removeClass( 'left' );

            if( pos_left + tooltip.outerWidth() > $( window ).width() )
            {
                pos_left = target.offset().left - tooltip.outerWidth() + target.outerWidth() / 2 + 20;
                tooltip.addClass( 'right' );
            }
            else
                tooltip.removeClass( 'right' );

            if( pos_top < 0 )
            {
                var pos_top  = target.offset().top + target.outerHeight();
                tooltip.addClass( 'top' );
            }
            else
                tooltip.removeClass( 'top' );

            tooltip.css( { left: pos_left, top: pos_top } )
                   .animate( { top: '+=10', opacity: 1 }, 50 );
        };

        init_tooltip();
        $( window ).resize( init_tooltip );

        tooltip.bind( 'click tap', remove_tooltip );
    });
});

I made three important changes:

  1. I search for each and every click/tap on the whole site and I apply the removed function only when the ABBR is selected and the Tooltip is defined.

    $(document.body).on('click tap', function (e) { });

  2. I tracked the click/tap on the ABBRs and I displayed the Tooltip:

    $(document.body).on('click tap', '[rel=tooltip]', function () { });

  3. I make the remove function public. Also, I checked if the Tooltip is initialized .

You could ask me: why did you replace all mouse events by clicks or taps?

Generally, in Mobile Development you don't track Mouse events such such as: mouseenter, mouseleave, etc. Because people hardly ever use a mouse for their daily activities, we use more our fingers with taps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM