Trong thiết kế website, menu là một thành phần hầu như không thể thiếu. Đặc biệt với sự phát triển mạnh mẽ của công nghệ như hiện nay, việc tạo menu đáp ứng được tính tương thích với mọi thiết bị là vấn đề các webmaster rất quan tâm
Qua khảo sát một vài trang cung cấp template blogger miễn phí nổi tiếng như templatesyard, sora templates,... tôi thấy họ làm rất tỉ mỉ, trau chuốt menu cho PC, tuy nhiên đến phần menu cho mobile thì làm rất qua loa chỉ sử dụng plugin selectnav (chuyển menu về thẻ select) để responsive, nên khi chuyển sang giao diện mobile làm trang trở nên rất thô
Sau
Hiện nay trên mạng không thiếu cách tạo menu có khả năng responsive, và trong bài viết này tôi sẽ giới thiệu cho bạn một plugin giúp menu có thể hiển thị tốt trên mobile
Trước tiên bạn xem demo, thay đổi kích thước trang demo để kiểm tra responsive
Trong bài viết này, menu sử dụng font awesome 4, nếu bạn dùng phiên bản 5 thì phải cập nhật lại code file js
Để tích hợp được menu này bạn chỉ cần làm như sau
Tích hợp jQuery nếu chưa có
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js' type='text/javascript'/>
CSS
.stellarnav,.stellarnav li{position:relative;line-height:normal} .stellarnav{width:100%;z-index:9900} .stellarnav ul{margin:0;padding:0;text-align:center} .stellarnav li{list-style:none;display:block;margin:0;padding:0;vertical-align:middle} .stellarnav li a{padding:15px;display:block;text-decoration:none;color:#777;font-size:inherit;font-family:inherit;box-sizing:border-box;-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;transition:all .3s ease-out} .stellarnav>ul>li{display:inline-block} .stellarnav>ul>li>a{padding:20px 40px} .stellarnav ul ul{top:auto;width:220px;position:absolute;z-index:9900;text-align:left;display:none;background:#ddd} .stellarnav li li{display:block} .stellarnav ul ul ul{top:0;left:220px} .stellarnav>ul>li:hover>ul>li:hover>ul{opacity:1;visibility:visible;top:0} .stellarnav>ul>li.drop-left>ul{right:0} .stellarnav li.drop-left ul ul{left:auto;right:220px} .stellarnav.light,.stellarnav.light ul ul{background:rgba(255,255,255,1)} .stellarnav.light li a{color:#000} .stellarnav.light .call-btn-mobile,.stellarnav.light .close-menu,.stellarnav.light .location-btn-mobile,.stellarnav.light .menu-toggle{color:rgba(0,0,0,1)} .stellarnav.dark,.stellarnav.dark ul ul{background:rgba(0,0,0,1)} .stellarnav.dark li a{color:#FFF} .stellarnav.dark .call-btn-mobile,.stellarnav.dark .close-menu,.stellarnav.dark .location-btn-mobile,.stellarnav.dark .menu-toggle{color:rgba(255,255,255,1)} .stellarnav.fixed{position:fixed;width:100%;top:0;left:0;z-index:9999} body.stellarnav-noscroll-x{overflow-x:hidden} .stellarnav li.has-sub>a:after{content:'\f107';font-family:FontAwesome;margin-left:10px} .stellarnav li li.has-sub>a:after{content:'\f105';font-family:FontAwesome;margin-left:10px} .stellarnav li.drop-left li.has-sub>a:after{float:left;content:'\f104';font-family:FontAwesome;margin-right:10px} .stellarnav.hide-arrows li li.has-sub>a:after,.stellarnav.hide-arrows li.drop-left li.has-sub>a:after,.stellarnav.hide-arrows li.has-sub>a:after{display:none} .stellarnav .dd-toggle{display:none;position:absolute;top:0;right:0;padding:0;width:48px;height:48px;text-align:center;z-index:9999;border:0} .stellarnav .dd-toggle i{position:absolute;margin:auto;top:33%;left:0;right:0;-webkit-transition:transform .3s ease-out;-moz-transition:transform .3s ease-out;transition:transform .3s ease-out} .stellarnav.mobile>ul>li>a.dd-toggle{padding:0} .stellarnav li.call-btn-mobile,.stellarnav li.location-btn-mobile{display:none} .stellarnav li.open>a.dd-toggle i{-webkit-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg);transform:rotate(135deg)} .stellarnav .call-btn-mobile,.stellarnav .close-menu,.stellarnav .location-btn-mobile,.stellarnav .menu-toggle{display:none;text-transform:uppercase;text-decoration:none;color:#777;padding:15px;box-sizing:border-box} .stellarnav .full{width:100%} .stellarnav .half{width:50%} .stellarnav .third{width:33%;text-align:center} .stellarnav .location-btn-mobile.third{text-align:center} .stellarnav .location-btn-mobile.half{text-align:right} .stellarnav.light .half,.stellarnav.light .third{border-left:1px solid rgba(0,0,0,.15)} .stellarnav.light.left .half,.stellarnav.light.left .third,.stellarnav.light.right .half,.stellarnav.light.right .third{border-bottom:1px solid rgba(0,0,0,.15)} .stellarnav.light .half:first-child,.stellarnav.light .third:first-child{border-left:0} .stellarnav.dark .half,.stellarnav.dark .third{border-left:1px solid rgba(255,255,255,.15)} .stellarnav.dark.left .half,.stellarnav.dark.left .third,.stellarnav.dark.right .half,.stellarnav.dark.right .third{border-bottom:1px solid rgba(255,255,255,.15)} .stellarnav.dark.left .menu-toggle,.stellarnav.dark.right .menu-toggle,.stellarnav.light.left .menu-toggle,.stellarnav.light.right .menu-toggle{border-bottom:0} .stellarnav.dark .half:first-child,.stellarnav.dark .third:first-child{border-left:0} .stellarnav.mobile,.stellarnav.mobile.fixed{position:static} .stellarnav.mobile ul{position:relative;display:none;text-align:left;background:rgba(221,221,221,1)} .stellarnav.mobile.active>ul,.stellarnav.mobile>ul>li{display:block} .stellarnav.mobile.active{padding-bottom:0} .stellarnav.mobile>ul>li>a{padding:15px} .stellarnav.mobile ul ul{position:relative;opacity:1;visibility:visible;width:auto;display:none;-moz-transition:none;-webkit-transition:none;-o-transition:color 0 ease-in;transition:none} .stellarnav.mobile ul ul ul{left:auto;top:auto} .stellarnav.mobile li.drop-left ul ul{right:auto} .stellarnav.mobile li a{border-bottom:1px solid rgba(255,255,255,.15)} .stellarnav.mobile li.has-sub a{padding-right:50px} .stellarnav.mobile>ul{border-top:1px solid rgba(255,255,255,.15)} .stellarnav.mobile.light li a{border-bottom:1px solid rgba(0,0,0,.15)} .stellarnav.mobile.light>ul{border-top:1px solid rgba(0,0,0,.15)} .stellarnav.mobile li a.dd-toggle,.stellarnav.mobile.light li a.dd-toggle{border:0} .stellarnav.mobile .call-btn-mobile,.stellarnav.mobile .close-menu,.stellarnav.mobile .dd-toggle,.stellarnav.mobile .location-btn-mobile,.stellarnav.mobile .menu-toggle{display:inline-block} .stellarnav.mobile li.call-btn-mobile{border-right:1px solid rgba(255,255,255,.1);box-sizing:border-box} .stellarnav.mobile li.call-btn-mobile,.stellarnav.mobile li.location-btn-mobile{display:inline-block;width:50%;text-transform:uppercase;text-align:center} .stellarnav.mobile li.call-btn-mobile.full,.stellarnav.mobile li.location-btn-mobile.full{display:block;width:100%;text-transform:uppercase;border-right:0;text-align:left} .stellarnav.mobile li.call-btn-mobile i,.stellarnav.mobile li.location-btn-mobile i{margin-right:5px} .stellarnav.mobile.light ul{background:rgba(255,255,255,1)} .stellarnav.mobile.dark ul{background:rgba(0,0,0,1)} .stellarnav.mobile.dark ul ul{background:rgba(255,255,255,.08)} .stellarnav.mobile.light li.call-btn-mobile{border-right:1px solid rgba(0,0,0,.1)} .stellarnav.mobile.top{position:absolute;width:100%;top:0;left:0;z-index:9999} .stellarnav.mobile li li.has-sub>a:after,.stellarnav.mobile li.drop-left li.has-sub>a:after,.stellarnav.mobile li.has-sub>a:after{display:none} .stellarnav.mobile.left>ul,.stellarnav.mobile.right>ul{position:fixed;top:0;bottom:0;width:100%;max-width:280px;overflow-x:hidden;overflow-y:auto;-webkit-overflow-scrolling:touch} .stellarnav.mobile.right>ul{right:0} .stellarnav.mobile.left .close-menu,.stellarnav.mobile.right .close-menu{display:inline-block;text-align:right} .stellarnav.mobile.left>ul{left:0} .stellarnav.mobile.left .call-btn-mobile.half,.stellarnav.mobile.left .call-btn-mobile.third,.stellarnav.mobile.left .close-menu.half,.stellarnav.mobile.left .close-menu.third,.stellarnav.mobile.left .location-btn-mobile.half,.stellarnav.mobile.left .location-btn-mobile.third,.stellarnav.mobile.right .call-btn-mobile.half,.stellarnav.mobile.right .call-btn-mobile.third,.stellarnav.mobile.right .close-menu.half,.stellarnav.mobile.right .close-menu.third,.stellarnav.mobile.right .location-btn-mobile.half,.stellarnav.mobile.right .location-btn-mobile.third{text-align:center} .stellarnav.mobile.left .menu-toggle.half,.stellarnav.mobile.left .menu-toggle.third,.stellarnav.mobile.right .menu-toggle.half,.stellarnav.mobile.right .menu-toggle.third{text-align:left} .stellarnav.mobile.left .close-menu.third span,.stellarnav.mobile.right .close-menu.third span{display:none} .stellarnav.desktop li.mega li{display:inline-block;vertical-align:top;margin-left:-4px} .stellarnav.desktop li.mega li li{display:block;margin-left:0} .stellarnav.desktop li.mega ul ul{width:auto} .stellarnav.desktop>ul>li.mega{position:inherit} .stellarnav.desktop>ul>li.mega>ul{width:100%} .stellarnav.desktop>ul>li.mega>ul li.has-sub ul{display:block;position:relative;left:auto} .stellarnav.desktop>ul>li.mega>ul>li{padding-bottom:15px;box-sizing:border-box} .stellarnav.desktop li.mega li li a{padding:5px 15px} .stellarnav.desktop li.mega li.has-sub a:after{display:none} .stellarnav.desktop>ul>li.mega>ul>li>a{color:#ff0} @media only screen and (max-width:768px){.stellarnav{overflow:hidden;display:block}.stellarnav ul{position:relative;display:none}} @media only screen and (max-width:420px){.stellarnav.mobile .call-btn-mobile.third span,.stellarnav.mobile .location-btn-mobile.third span{display:none}}
Chèn code js trước thẻ đóng </head>
<script type='text/javascript'>//<![CDATA[ !function(n){n.fn.stellarNav=function(i,e,s){nav=n(this),e=n(window).width();var a=n.extend({theme:"plain",breakpoint:768,menuLabel:"Menu",sticky:!1,position:"static",openingSpeed:250,closingDelay:250,showArrows:!0,phoneBtn:"",locationBtn:"",closeBtn:!1,scrollbarFix:!1},i);return this.each(function(){if("light"!=a.theme&&"dark"!=a.theme||nav.addClass(a.theme),a.breakpoint&&(s=a.breakpoint),a.menuLabel&&(menuLabel=a.menuLabel),a.phoneBtn&&a.locationBtn)var i="third";else if(a.phoneBtn||a.locationBtn)i="half";else i="full";if("right"==a.position||"left"==a.position?nav.prepend('<a href="#" class="menu-toggle"><i class="fa fa-bars"></i> '+menuLabel+"</a>"):nav.prepend('<a href="#" class="menu-toggle '+i+'"><i class="fa fa-bars"></i> '+menuLabel+"</a>"),a.phoneBtn&&"right"!=a.position&&"left"!=a.position){var t='<a href="tel:'+a.phoneBtn+'" class="call-btn-mobile '+i+'"><i class="fa fa-phone"></i> <span>Call Us</span></a>';nav.find("a.menu-toggle").after(t)}if(a.locationBtn&&"right"!=a.position&&"left"!=a.position){t='<a href="'+a.locationBtn+'" class="location-btn-mobile '+i+'" target="_blank"><i class="fa fa-map-marker"></i> <span>Location</span></a>';nav.find("a.menu-toggle").after(t)}if(a.sticky&&(navPos=nav.offset().top,e>=s&&n(window).bind("scroll",function(){n(window).scrollTop()>navPos?nav.addClass("fixed"):nav.removeClass("fixed")})),"top"==a.position&&nav.addClass("top"),"left"==a.position||"right"==a.position){var l='<a href="#" class="close-menu '+i+'"><i class="fa fa-close"></i> <span>Close</span></a>',o='<a href="tel:'+a.phoneBtn+'" class="call-btn-mobile '+i+'"><i class="fa fa-phone"></i></a>',d='<a href="'+a.locationBtn+'" class="location-btn-mobile '+i+'" target="_blank"><i class="fa fa-map-marker"></i></a>';nav.find("ul:first").prepend(l),a.locationBtn&&nav.find("ul:first").prepend(d),a.phoneBtn&&nav.find("ul:first").prepend(o)}"right"==a.position&&nav.addClass("right"),"left"==a.position&&nav.addClass("left"),a.showArrows||nav.addClass("hide-arrows"),a.closeBtn&&"right"!=a.position&&"left"!=a.position&&nav.find("ul:first").append('<li><a href="#" class="close-menu"><i class="fa fa-close"></i> Close Menu</a></li>'),a.scrollbarFix&&n("body").addClass("stellarnav-noscroll-x"),n(".menu-toggle").on("click",function(i){i.preventDefault(),"left"==a.position||"right"==a.position?(nav.find("ul:first").stop(!0,!0).fadeToggle(a.openingSpeed),nav.toggleClass("active"),nav.hasClass("active")&&nav.hasClass("mobile")&&n(document).on("click",function(i){nav.hasClass("mobile")&&(n(i.target).closest(nav).length||(nav.find("ul:first").stop(!0,!0).fadeOut(a.openingSpeed),nav.removeClass("active")))})):(nav.find("ul:first").stop(!0,!0).slideToggle(a.openingSpeed),nav.toggleClass("active"))}),n(".close-menu").click(function(){nav.removeClass("active"),"left"==a.position||"right"==a.position?nav.find("ul:first").stop(!0,!0).fadeToggle(a.openingSpeed):nav.find("ul:first").stop(!0,!0).slideUp(a.openingSpeed).toggleClass("active")}),nav.find("li a").each(function(){n(this).next().length>0&&n(this).parent("li").addClass("has-sub").append('<a class="dd-toggle" href="#"><i class="fa fa-plus"></i></a>')}),nav.find("li .dd-toggle").on("click",function(i){i.preventDefault(),n(this).parent("li").children("ul").stop(!0,!0).slideToggle(a.openingSpeed),n(this).parent("li").toggleClass("open")});var f=function(){nav.find("li").unbind("mouseenter"),nav.find("li").unbind("mouseleave")};parentItems=nav.find("> ul > li");var r=function(){n(parentItems).each(function(){n(this).hasClass("mega")?(n(this).on("mouseenter",function(){n(this).find("ul").first().stop(!0,!0).slideDown(a.openingSpeed)}),n(this).on("mouseleave",function(){n(this).find("ul").first().stop(!0,!0).slideUp(a.openingSpeed)})):(n(this).on("mouseenter",function(){n(this).children("ul").stop(!0,!0).slideDown(a.openingSpeed)}),n(this).on("mouseleave",function(){n(this).children("ul").stop(!0,!0).delay(a.closingDelay).slideUp(a.openingSpeed)}),n(this).find("li.has-sub").on("mouseenter",function(){n(this).children("ul").stop(!0,!0).slideDown(a.openingSpeed)}),n(this).find("li.has-sub").on("mouseleave",function(){n(this).children("ul").stop(!0,!0).delay(a.closingDelay).slideUp(a.openingSpeed)}))})};function h(){window.innerWidth<=s?(f(),nav.addClass("mobile"),nav.removeClass("desktop"),!nav.hasClass("active")&&nav.find("ul:first").is(":visible")&&nav.find("ul:first").hide(),nav.find("li.mega").each(function(){n(this).find("ul").first().removeAttr("style"),n(this).find("ul").first().children().removeAttr("style")})):(nav.addClass("desktop"),nav.removeClass("mobile"),nav.hasClass("active")&&nav.removeClass("active"),!nav.hasClass("active")&&nav.find("ul:first").is(":hidden")&&nav.find("ul:first").show(),n("li.open").removeClass("open").find("ul:visible").hide(),f(),r(),navWidth=0,navIniPos=0,n(parentItems).each(function(){navWidth+=n(this)[0].getBoundingClientRect().width,navWidth=Math.round(navWidth),n(this).hasClass("mega")&&(n(this).find("ul").first().css({left:navIniPos}),numCols=n(this).attr("data-columns"),2==numCols?n(this).find("li.has-sub").width("50%"):3==numCols?n(this).find("ul").first().children().width("33.33%"):4==numCols?n(this).find("ul").first().children().width("25%"):5==numCols?n(this).find("ul").first().children().width("20%"):6==numCols?n(this).find("ul").first().children().width("16.66%"):7==numCols?n(this).find("ul").first().children().width("14.28%"):8==numCols?n(this).find("ul").first().children().width("12.5%"):n(this).find("ul").first().children().width("25%"))}),parentItems.hasClass("mega")&&nav.find("li.mega ul").css({"max-width":navWidth}))}h(),n(window).on("resize",function(){h()})})}}(jQuery); //]]></script>
Cấu trúc HTML cho menu
<div class="stellarnav"> <ul> <li><a href="">Dropdown</a> <ul> <li><a href="#">How deep?</a> <ul> <li><a href="#">Item</a> <ul> <li><a href="#">Item</a> <ul> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> </ul> </li> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> </ul> </li> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> </ul> </li> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> <li><a href="#">Here's a very long item. It can be as long as you want</a></li> <li><a href="#">Item</a></li> </ul> </li> <li><a href="">Item 2</a></li> <li><a href="">Item 3</a></li> <li><a href="">Item 4</a></li> <li><a href="">Item 5</a></li> <li><a href="">Item 6</a></li> </ul> </div>
Như bạn thấy, plugin này chỉ có một yêu cầu duy nhất đó là bạn đặt 1 thẻ div bao lấy toàn bộ thanh menu, không có yêu cầu class cho các thẻ ul li (kiểu như sub-menu, dropdown,...). Điều này sẽ rất tiện lợi cho bạn nào sử dụng menu đa cấp lớn muốn chuyển qua menu này
Cuối dùng để bật menu bạn dán đoạn js sau (dán sau đoạn js bên trên)
<script type='text/javascript'>//<![CDATA[ jQuery('.stellarnav').stellarNav({ theme: 'plain', breakpoint: 768, menuLabel: 'Menu', sticky: false, position: 'static', openingSpeed: 250, closingDelay: 250, showArrows: true, phoneBtn: '', locationBtn: '', closeBtn: false, scrollbarFix: false }); //]]></script>
Trong đó bảng config sẽ như sau
Attribute | Type | Default | Description |
---|---|---|---|
theme |
String | plain |
Adds default color to nav. [plain, light, dark] |
breakpoint |
Integer | 768 |
Number in pixels to determine when the nav should turn mobile friendly. |
menuLabel |
String | Menu |
Label (text) for the mobile nav. |
sticky |
Boolean | false |
Makes nav sticky on scroll. |
position |
String | static |
[static, top, left, right] - When set to 'top', this forces the mobile nav to be placed absolutely on the very top of page. When set to 'left' or 'right', mobile nav fades in/out from left or right, accordingly. |
openingSpeed |
Integer | 250 |
Controls how fast the dropdowns open in milliseconds. |
closingDelay |
Integer | 250 |
Controls how long the dropdowns stay open for in milliseconds. |
showArrows |
Boolean | true |
Shows dropdown arrows next to the items that have sub menus. |
phoneBtn |
String | [empty] |
Adds a click-to-call phone link to the top of menu - i.e.: "18009084500". |
locationBtn |
String | [empty] |
Adds a location link to the top of menu - i.e.: "/location/", "http://site.com/contact-us/". |
closeBtn |
Boolean | false |
Adds a close button to the end of nav. |
scrollbarFix |
Boolean | false |
Fixes horizontal scrollbar issue on very long navs. |
* Mở rộng
Trong thiết kế menu đa cấp, sub-menu thường trượt từ trái sang phải, nếu vô tình parent-menu nằm ở tận cùng bên phải có thể sub-menu của chúng sẽ bị lỗi hiển thị. Trong plugin này, ta có thể khắc phục được điều đó bằng việc đặt class như sau
<div class="stellarnav"> <ul> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> <li><a href="#">Item</a></li> <li class="drop-left"><a href="#">Last Dropdown Item</a> <ul> <li><a href="#">Item</a></li> <li><a href="#">Item</a> <ul> <li><a href="#">Drop left menu item</a></li> <li><a href="#">Drop left menu item</a></li> </ul> </li> </ul> </li> </ul> </div>
Khi đó, sub-menu của class drop-left khi hover sẽ trượt từ phải sang trái, giải quyết được lỗi hiển thị
* Vấn đề tích hợp
Demo và cò kéo thì có vẻ hay, tuy nhiên vấn đề tích hợp vào blog thực sự không đơn giản do nó còn chịu ảnh hưởng rất nhiều từ css blog. Vì thế điều này hoàn toàn phụ thuộc vào bạn, tôi chỉ có một số lời khuyên như sau
- Xóa toàn bộ code html, js, css của menu cũ
- Lựa chọn trước cho mình theme của menu để phù hợp với blog của bạn, vì plugin này cung cấp cho bạn 3 loại plain, light, dark, bạn chọn trước theme sau đó chỉ tập trung vào sửa css của theme đó
- Để lại bình luận nếu bạn gặp khó khăn
Demo 1 template mà tôi đã tích hợp thành công: https://hunghoangvan1001nd.blogspot.com
KẾT LUẬN: đây có thể coi là một thanh menu hoàn chỉnh (đa cấp, responsive) rất đáng để ta bỏ công ra tích hợp vào website. Khi tôi vào một website nào đó tôi thường rất để ý tới thanh menu của họ và cũng không quên check responsive, vì nếu là một người cẩn thận tỉ mỉ, họ rất ít khi để người khác thấy được sơ hở của mình. Việc phá cách khỏi khuôn khổ mỗi template cung cấp cũng giúp bạn khẳng định được giá trị của mình hơn
Chúc các bạn thành công !
1. Không vi phạm luật pháp nước CHXHCN Việt Nam
2. Không vi phạm thuần phong mỹ tục Việt Nam
3. Không bàn luận vấn đề liên quan đến tôn giáo, chính trị
4. Không đả kích, chửi bới hay đưa ra những lời nói không phù hợp với mục tiêu của website
5. Không bình luận với mục đích quảng cáo, trao đổi, mua bán
6. Khuyến khích sử dụng Tiếng Việt có dấu, hạn chế sử dụng tiếng lóng, viết tắt
7. Khi cần sự trợ giúp, vui lòng miêu tả chi tiết lỗi và để lại link đính kèm, tránh nói chung chung gây mất thời gian cho đôi bên