Tạo MENU dạng tooltip với CSS và Jquery

 Xem demo  Download demo

Hôm nay mình tiếp tục mang đến cho các bạn một hiệu ứng cực kỳ hữu ích và rất hay sử dụng trên các trang web , đó là hiệu ứng tool tip cho các submenu. Với hiệu ứng này, rất phù hợp cho các bạn áp dụng vào các menu có độ phân cấp cao trên trang web hay blog của các bạn.

css, jquery menu, menu tooltip

 

HTML

Để minh họa menu có độ phân cấp, chúng ta sẽ sử dụng khung chuẩn html như sau :

<ul id="cbp-tm-menu" class="cbp-tm-menu">

    <li>

        <a href="#">Home</a>

    </li>

    <li>

        <a href="#">Veggie made</a>

        <ul class="cbp-tm-submenu">

            <li><a href="#" class="cbp-tm-icon-archive">Sorrel desert</a></li>

            <li><a href="#" class="cbp-tm-icon-cog">Raisin kakadu</a></li>

            <li><a href="#" class="cbp-tm-icon-location">Plum salsify</a></li>

            <li><a href="#" class="cbp-tm-icon-users">Bok choy celtuce</a></li>

            <li><a href="#" class="cbp-tm-icon-earth">Onion endive</a></li>

            <li><a href="#" class="cbp-tm-icon-location">Bitterleaf</a></li>

            <li><a href="#" class="cbp-tm-icon-mobile">Sea lettuce</a></li>

        </ul>

    </li>

    <li>

        <a href="#">Pepper tatsoi</a>

        <ul class="cbp-tm-submenu">

            <li><a href="#" class="cbp-tm-icon-archive">Brussels sprout</a></li>

            <li><a href="#" class="cbp-tm-icon-cog">Kakadu lemon</a></li>

            <li><a href="#" class="cbp-tm-icon-link">Juice green</a></li>

            <li><a href="#" class="cbp-tm-icon-users">Wine fruit</a></li>

            <li><a href="#" class="cbp-tm-icon-earth">Garlic mint</a></li>

            <li><a href="#" class="cbp-tm-icon-location">Zucchini garnish</a></li>

            <li><a href="#" class="cbp-tm-icon-mobile">Sea lettuce</a></li>

        </ul>

    </li>

    <li>

        <a href="#">Sweet melon</a>

        <ul class="cbp-tm-submenu">

            <li><a href="#" class="cbp-tm-icon-screen">Sorrel desert</a></li>

            <li><a href="#" class="cbp-tm-icon-mail">Raisin kakadu</a></li>

            <li><a href="#" class="cbp-tm-icon-contract">Plum salsify</a></li>

            <li><a href="#" class="cbp-tm-icon-pencil">Bok choy celtuce</a></li>

            <li><a href="#" class="cbp-tm-icon-article">Onion endive</a></li>

            <li><a href="#" class="cbp-tm-icon-clock">Bitterleaf</a></li>

        </ul>

    </li>

</ul>

CSS

Các submenu sẽ được kèm theo các icon, các bạn có thể tìm kiếm các icon ở mục download.

/* Iconfont made with icomoon.com */

@font-face {

    font-family: 'cbp-tmicons';

    src:url('../fonts/tmicons/cbp-tmicons.eot');

    src:url('../fonts/tmicons/cbp-tmicons.eot?#iefix') format('embedded-opentype'),

        url('../fonts/tmicons/cbp-tmicons.woff') format('woff'),

        url('../fonts/tmicons/cbp-tmicons.ttf') format('truetype'),

        url('../fonts/tmicons/cbp-tmicons.svg#cbp-tmicons') format('svg');

    font-weight: normal;

    font-style: normal;
}


/* reset  list style */

.cbp-tm-menu,

.cbp-tm-menu ul {
    list-style: none;
}

/* set menu position; change here to set to relative or float, etc. */

.cbp-tm-menu {

    display: block;

    position: absolute;

    z-index: 1000;

    bottom: 0;

    width: 100%;

    background: #47a3da;

    text-align: right;

    padding: 0 2em;

    margin: 0;

    text-transform: capitalize;

}

/* first level menu items */

.cbp-tm-menu > li {

    display: inline-block;

    margin: 0 2.6em;

    position: relative;
}

.cbp-tm-menu > li > a {

    line-height: 4em;

    padding: 0 0.3em;

    font-size: 1.2em;

    display: block;

    color: #fff;
}

.no-touch .cbp-tm-menu > li > a:hover,

.no-touch .cbp-tm-menu > li > a:active {

    color: #02639d;
}

/* sumbenu with transitions */

.cbp-tm-submenu {

    position: absolute;

    display: block;

    visibility: hidden;

    opacity: 0;

    padding: 0;

    text-align: left;

    pointer-events: none;

    -webkit-transition: visibility 0s, opacity 0s;

    -moz-transition: visibility 0s, opacity 0s;

    transition: visibility 0s, opacity 0s;
}

.cbp-tm-show .cbp-tm-submenu {

    width: 16em;

    left: 50%;

    margin: 0 0 0 -8em;

    opacity: 1;

    visibility: visible;

    pointer-events: auto;

    -webkit-transition: visibility 0s, opacity 0.3s;

    -moz-transition: visibility 0s, opacity 0.3s;

    transition: visibility 0s, opacity 0.3s;
}

.cbp-tm-show-above .cbp-tm-submenu {

    bottom: 100%;

    padding-bottom: 10px;
}

.cbp-tm-show-below .cbp-tm-submenu {

    top: 100%;

    padding-top: 10px;
}

/* extreme cases: not enough space on the sides */

.cbp-tm-nospace-right .cbp-tm-submenu {

    right: 0;

    left: auto;
}

.cbp-tm-nospace-left .cbp-tm-submenu {

    left: 0;
}

/* last menu item has to fit on the screen */

.cbp-tm-menu > li:last-child .cbp-tm-submenu {

    right: 0;
}

/*

arrow: depending on where the menu will be shown, we set

the right position for the arrow

*/

.cbp-tm-submenu:after {

    border: solid transparent;

    content: " ";

    height: 0;

    width: 0;

    position: absolute;

    pointer-events: none;
}

.cbp-tm-show-above .cbp-tm-submenu:after {

    top: 100%;

    margin-top: -10px;
}

.cbp-tm-show-below .cbp-tm-submenu:after {

    bottom: 100%;

    margin-bottom: -10px;
}

.cbp-tm-submenu:after {

    border-color: transparent;

    border-width: 16px;

    margin-left: -16px;

    left: 50%;
}

.cbp-tm-show-above .cbp-tm-submenu:after {

    border-top-color: #fff;
}

.cbp-tm-show-below .cbp-tm-submenu:after {

    border-bottom-color: #fff;
}

.cbp-tm-submenu > li {

    display: block;

    background: #fff;
}

.cbp-tm-submenu > li > a {

    padding: 5px 2.3em 5px 0.6em; /* top/bottom paddings in 'em' cause a tiny "jump" in Chrome on Win */

    display: block;

    font-size: 1.2em;

    position: relative;

    color: #47a3da;

    border: 4px solid #fff;

    -webkit-transition: all 0.2s;

    -moz-transition: all 0.2s;

    transition: all 0.2s;
}

.no-touch .cbp-tm-submenu > li > a:hover,

.no-touch .cbp-tm-submenu > li > a:active {

    color: #fff;

    background: #47a3da;
}

/* the icons (main level menu icon and sublevel icons) */

.cbp-tm-submenu li a:before,

.cbp-tm-menu > li > a:before {

    font-family: 'cbp-tmicons';

    speak: none;

    font-style: normal;

    font-weight: normal;

    font-variant: normal;

    text-transform: none;

    line-height: 1;

    vertical-align: middle;

    margin-right: 0.6em;

    -webkit-font-smoothing: antialiased;
}

.cbp-tm-submenu li a:before {

    position: absolute;

    top: 50%;

    margin-top: -0.5em;

    right: 0.5em;
}

.cbp-tm-menu > li > a:not(:only-child):before {

    content: "\f0c9";

    font-size: 60%;

    opacity: 0.3;
}

.cbp-tm-icon-archive:before {

    content: "\e002";
}

.cbp-tm-icon-cog:before {

    content: "\e003";
}

.cbp-tm-icon-users:before {

    content: "\e004";
}


.cbp-tm-icon-earth:before {

    content: "\e005";

}

.cbp-tm-icon-location:before {

    content: "\e006";
}

.cbp-tm-icon-mobile:before {

    content: "\e007";
}

.cbp-tm-icon-screen:before {

    content: "\e008";
}

.cbp-tm-icon-mail:before {

    content: "\e009";

}

.cbp-tm-icon-contract:before {

    content: "\e00a";
}

.cbp-tm-icon-pencil:before {

    content: "\e00b";
}

.cbp-tm-icon-article:before {

    content: "\e00c";
}

.cbp-tm-icon-clock:before {

    content: "\e00d";
}

.cbp-tm-icon-videos:before {

    content: "\e00e";

}



.cbp-tm-icon-pictures:before {

    content: "\e00f";
}

.cbp-tm-icon-link:before {

    content: "\e010";
}

.cbp-tm-icon-refresh:before {

    content: "\e011";
}

.cbp-tm-icon-help:before {

    content: "\e012";
}

/* Media Queries */

@media screen and (max-width: 55.6875em) {

    .cbp-tm-menu {

        font-size: 80%;
    }
}



@media screen and (max-height: 25.25em), screen and (max-width: 44.3125em) {

    .cbp-tm-menu {

        font-size: 100%;

        position: relative;

        text-align: center;

        padding: 0;

        top: auto;
    }

    .cbp-tm-menu > li {

        display: block;

        margin: 0;

        border-bottom: 4px solid #3793ca;
    }

    .cbp-tm-menu > li:first-child {

        border-top: 4px solid #3793ca;
    }

    li.cbp-tm-show > a,

    .no-touch .cbp-tm-menu > li > a:hover,

    .no-touch .cbp-tm-menu > li > a:active {

        color: #fff;

        background: #02639d;
    }

    .cbp-tm-submenu {

        position: relative;

        display: none;

        width: 100%;
    }

    .cbp-tm-submenu > li {

        padding: 0;

    }

    .cbp-tm-submenu > li > a {

        padding: 0.6em 2.3em 0.6em 0.6em;

        border: none;

        border-bottom: 2px solid #6fbbe9;
    }

    .cbp-tm-submenu:after {

        display: none;
    }

    .cbp-tm-menu .cbp-tm-show .cbp-tm-submenu {

        display: block;

        width: 100%;

        left: 0;

        margin: 0;

        padding: 0;

        bottom: auto;

        top: auto;
    }
}

jQuery

Để menu có thể có thể tạo hiệu ứng tooltip , chúng ta cần có thêm đoạn jQuery sau :

/**

 * Licensed under the MIT license.

 * http://www.opensource.org/licenses/mit-license.php

 *

 */

;( function( window ) {

    'use strict';

    var document = window.document,

        docElem = document.documentElement;

    function extend( a, b ) {

        for( var key in b ) {

            if( b.hasOwnProperty( key ) ) {

                a[key] = b[key];
            }
        }
        return a;
    }

    // from https://github.com/ryanve/response.js/blob/master/response.js

    function getViewportH() {

        var client = docElem['clientHeight'],

            inner = window['innerHeight'];

        if( client < inner )

            return inner;

        else

            return client;
    }

    // http://stackoverflow.com/a/11396681/989439

    function getOffset( el ) {

        return el.getBoundingClientRect();
    }

    // http://snipplr.com/view.php?codeview&id=5259

    function isMouseLeaveOrEnter(e, handler) {

        if (e.type != 'mouseout' && e.type != 'mouseover') return false;

        var reltg = e.relatedTarget ? e.relatedTarget :

        e.type == 'mouseout' ? e.toElement : e.fromElement;

        while (reltg && reltg != handler) reltg = reltg.parentNode;

        return (reltg != handler);
    }

    function cbpTooltipMenu( el, options ) {

        this.el = el;

        this.options = extend( this.defaults, options );

        this._init();
    }

    cbpTooltipMenu.prototype = {

        defaults : {

            // add a timeout to avoid the menu to open instantly

            delayMenu : 100
        },

        _init : function() {

            this.touch = Modernizr.touch;

            this.menuItems = document.querySelectorAll( '#' + this.el.id + ' > li' );

            this._initEvents();

        },

        _initEvents : function() {

            var self = this;

            Array.prototype.slice.call( this.menuItems ).forEach( function( el, i ) {

                var trigger = el.querySelector( 'a' );

                if( self.touch ) {

                    trigger.addEventListener( 'click', function( ev ) { self._handleClick( this, ev ); } );

                }
                else {

                    trigger.addEventListener( 'click', function( ev ) {

                        if( this.parentNode.querySelector( 'ul.cbp-tm-submenu' ) ) {

                            ev.preventDefault();

                        }

                    } );

                    el.addEventListener( 'mouseover', function(ev) { if( isMouseLeaveOrEnter( ev, this ) ) self._openMenu( this ); } );

                    el.addEventListener( 'mouseout', function(ev) { if( isMouseLeaveOrEnter( ev, this ) ) self._closeMenu( this ); } );

                }

            } );
        },

        _openMenu : function( el ) {

            var self = this;

            clearTimeout( this.omtimeout );

            this.omtimeout = setTimeout( function() {

                var submenu = el.querySelector( 'ul.cbp-tm-submenu' );

                if( submenu ) {

                    el.className = 'cbp-tm-show';

                    if( self._positionMenu( el ) === 'top' ) {

                        el.className += ' cbp-tm-show-above';

                    }

                    else {

                        el.className += ' cbp-tm-show-below';

                    }

                }

            }, this.touch ? 0 : this.options.delayMenu );



        },

        _closeMenu : function( el ) {

            clearTimeout( this.omtimeout );

            var submenu = el.querySelector( 'ul.cbp-tm-submenu' );

            if( submenu ) {

                // based on https://github.com/desandro/classie/blob/master/classie.js

                el.className = el.className.replace(new RegExp("(^|\\s+)" + "cbp-tm-show" + "(\\s+|$)"), ' ');

                el.className = el.className.replace(new RegExp("(^|\\s+)" + "cbp-tm-show-below" + "(\\s+|$)"), ' ');

                el.className = el.className.replace(new RegExp("(^|\\s+)" + "cbp-tm-show-above" + "(\\s+|$)"), ' ');

            }

        },

        _handleClick : function( el, ev ) {

            var item = el.parentNode,

                items = Array.prototype.slice.call( this.menuItems ),

                submenu = item.querySelector( 'ul.cbp-tm-submenu' )

            // first close any opened one..

            if( typeof this.current !== 'undefined' &&  items.indexOf( item ) !== this.current ) {

                this._closeMenu( this.el.children[ this.current ] );

                this.el.children[ this.current ].querySelector( 'ul.cbp-tm-submenu' ).setAttribute( 'data-open', 'false' );

            }

            if( submenu ) {

                ev.preventDefault();

                var isOpen = submenu.getAttribute( 'data-open' );

                if( isOpen === 'true' ) {

                    this._closeMenu( item );

                    submenu.setAttribute( 'data-open', 'false' );

                }

                else {

                    this._openMenu( item );

                    this.current = items.indexOf( item );

                    submenu.setAttribute( 'data-open', 'true' );

                }

            }

        },

        _positionMenu : function( el ) {

            // checking where's more space left in the viewport: above or below the element

            var vH = getViewportH(),

                ot = getOffset(el),

                spaceUp = ot.top ,

                spaceDown = vH - spaceUp - el.offsetHeight;

            return ( spaceDown <= spaceUp ? 'top' : 'bottom' );

        }
    }

    // add to global namespace

    window.cbpTooltipMenu = cbpTooltipMenu;

} )( window );

Để có thể hiểu và sử dụng code một cách hiệu quả nhất, các bạn nên download mã nguồn mà mình chia sẻ cho các bạn về máy để hiểu rõ hơn.

Chúc các bạn thành công !

 

 HỖ TRỢ TRỰC TUYẾN