View Full Version : Close Flex Level Drop Down Menu on touch-screen device?

06-30-2014, 06:43 PM
1) Script Title: Flex Level Drop Down Menu

2) Script URL (on DD): http://www.dynamicdrive.com/dynamicindex1/flexdropdown.htm

3) Describe problem: Once invoked, there's no way to close the menu on touch-screen mobile device

I just added this menu script on my website, but I noticed on my iPhone, once I open the menu, there's no way to close it. With the compact dropdown menu script, the menu closes if you click the link again. I'd like to get that same functionality with the flex menu, or at least close the menu when you touch anywhere outside of it. Any help would be much appreciated.


07-02-2014, 12:05 PM

07-02-2014, 02:10 PM
try (clicking outside the menu)

/* Flex Level Drop Down Menu
* Created: Jan 5th, 2010 by DynamicDrive.com. This notice must stay intact for usage
* Author: Dynamic Drive at http://www.dynamicdrive.com/
* Visit http://www.dynamicdrive.com/ for full source code

//Version 1.1 (Feb 19th, 2010): Each flex menu (UL) can now be associated with a link dynamically, and/or defined using JavaScript instead of as markup.
//Version 1.2 (July 2nd, 2011): Menu updated to work properly in popular mobile devices such as iPad/iPhone and Android tablets.
//Version 1.3 (Nov 28th, 2011): Script now dynamically adds a class of "selected" to the anchor link while its drop down menu is expanded, for easy styling of the anchor link during its "open" state.

//Usage: $(elementselector).addflexmenu('menuid', options)
//$('a.mylinks').addflexmenu('flexmenu1') //apply flex menu with ID "flexmenu1" to links with class="mylinks"


var flexdropdownmenu={
arrowpath: 'arrow.gif', //full URL or path to arrow image
animspeed: 200, //reveal animation speed (in milliseconds)
showhidedelay: [150, 150], //delay before menu appears and disappears when mouse rolls over it, in milliseconds

ismobile:navigator.userAgent.match(/(iPad)|(iPhone)|(iPod)|(android)|(webOS)/i) != null, //boolean check for popular mobile browsers
builtflexmenuids: [], //ids of flex menus already built (to prevent repeated building of same flex menu)

positionul:function($, $ul, e, $anchor){
var istoplevel=$ul.hasClass('jqflexmenu') //Bool indicating whether $ul is top level flex menu DIV
var docrightedge=$(document).scrollLeft()+$(window).width()-40 //40 is to account for shadows in FF
var docbottomedge=$(document).scrollTop()+$(window).height()-40
if (istoplevel){ //if main flex menu DIV
var offsets=$anchor.offset()
var anchorsetting=$anchor.data('setting')
var x=offsets.left+anchorsetting.useroffsets[0]+(anchorsetting.dir=="h"? $anchor.outerWidth() : 0) //x pos of main flex menu UL
var y=offsets.top+anchorsetting.useroffsets[1]+(anchorsetting.dir=="h"? 0 : $anchor.outerHeight())
x=(x+$ul.data('dimensions').w > docrightedge)? x-(anchorsetting.useroffsets[0]*2)-$ul.data('dimensions').w+$anchor.outerWidth()+(anchorsetting.dir=="h"? -($anchor.outerWidth()*2) : 0) : x //if not enough horizontal room to the ridge of the cursor
y=(y+$ul.data('dimensions').h > docbottomedge)? y-(anchorsetting.useroffsets[1]*2)-$ul.data('dimensions').h-$anchor.outerHeight()+(anchorsetting.dir=="h"? ($anchor.outerHeight()*2) : 0) : y
else{ //if sub level flex menu UL
var $parentli=$ul.data('$parentliref')
var parentlioffset=$parentli.offset()
var x=$ul.data('dimensions').parentliw //x pos of sub UL
var y=0
x=(parentlioffset.left+x+$ul.data('dimensions').w > docrightedge)? x-$ul.data('dimensions').parentliw-$ul.data('dimensions').w : x //if not enough horizontal room to the ridge parent LI
y=(parentlioffset.top+$ul.data('dimensions').h > docbottomedge)? y-$ul.data('dimensions').h+$ul.data('dimensions').parentlih : y
$ul.css({left:x, top:y})

showbox:function($, $target, $flexmenu, e){
$flexmenu.data('timers').showtimer=setTimeout(function(){$target.addClass('selected'); $flexmenu.show(flexdropdownmenu.animspeed)}, this.showhidedelay[0])

hidebox:function($, $target, $flexmenu){
$flexmenu.data('timers').hidetimer=setTimeout(function(){$target.removeClass('selected'); $flexmenu.hide(100)}, this.showhidedelay[1]) //hide flex menu plus all of its sub ULs

var o=e.target?e.target:e.srcElement,a=flexdropdownmenu.builtflexmenuids,z0=0;
while (o.parentNode){
if (flexdropdownmenu[o.id]){
for (;z0<a.length;z0++){

buildflexmenu:function($, $menu, $target){
$menu.css({display:'block', visibility:'hidden', zIndex:this.startzindex}).addClass('jqflexmenu').appendTo(document.body)
$menu.bind('mouseenter', function(){
$menu.bind('mouseleave', function(){ //hide menu when mouse moves out of it
flexdropdownmenu.hidebox($, $target, $menu)
$menu.data('dimensions', {w:$menu.outerWidth(), h:$menu.outerHeight()}) //remember main menu's dimensions
$menu.data('timers', {})
var $lis=$menu.find("ul").parent() //find all LIs within menu with a sub UL
var $li=$(this).css({zIndex: 1000+i})
var $subul=$li.find('ul:eq(0)').css({display:'block'}) //set sub UL to "block" so we can get dimensions
$subul.data('dimensions', {w:$subul.outerWidth(), h:$subul.outerHeight(), parentliw:this.offsetWidth, parentlih:this.offsetHeight})
$subul.data('$parentliref', $li) //cache parent LI of each sub UL
$subul.data('timers', {})
$li.data('$subulref', $subul) //cache sub UL of each parent LI
$li.children("a:eq(0)").append( //add arrow images
'<img src="'+flexdropdownmenu.arrowpath+'" class="rightarrowclass" style="border:0;" />'
$li.bind(flexdropdownmenu.triggerevt, function(e){ //show sub UL when mouse moves over parent LI
var $targetul=$(this).css('zIndex', ++flexdropdownmenu.startzindex).addClass("selected").data('$subulref')
if ($targetul.queue().length<=1){ //if 1 or less queued animations
flexdropdownmenu.positionul($, $targetul, e)
}, flexdropdownmenu.showhidedelay[0])
if (flexdropdownmenu.triggerevt=="click" && $(e.target).next('ul').length==1) //if LI being clicked on is a menu header
return false
$li.bind('mouseleave', function(e){ //hide sub UL when mouse moves out of parent LI
var $targetul=$(this).data('$subulref')
$targetul.data('timers').hidetimer=setTimeout(function(){$targetul.hide(100).data('$parentliref').removeClass('selected')}, flexdropdownmenu.showhidedelay[1])
$menu.find('ul').andSelf().css({display:'none', visibility:'visible'}) //collapse all ULs again
this.builtflexmenuids.push($menu.get(0).id) //remember id of flex menu that was just built

init:function($, $target, $flexmenu){
this.triggerevt=(this.ismobile)? "click" : "mouseenter"
this.showhidedelay[0]=(this.ismobile)? 0 : this.showhidedelay[0]
if (this.builtflexmenuids.length==0){ //only bind click event to document once
$(document).bind("click", function(e){
if (e.button==0){ //hide all flex menus (and their sub ULs) when left mouse button is clicked
if (jQuery.inArray($flexmenu.get(0).id, this.builtflexmenuids)==-1) //if this flex menu hasn't been built yet
this.buildflexmenu($, $flexmenu, $target)
if ($target.parents().filter('ul.jqflexmenu').length>0) //if $target matches an element within the flex menu markup, don't bind onflexmenu to that element
var useroffsets=$target.attr('data-offsets')? $target.attr('data-offsets').split(',') : [0,0] //get additional user offsets of menu
useroffsets=[parseInt(useroffsets[0]), parseInt(useroffsets[1])]
$target.data('setting', {dir: $target.attr('data-dir'), useroffsets: useroffsets}) //store direction (drop right or down) of menu plus user offsets
$target.bind(flexdropdownmenu.triggerevt, function(e){
$flexmenu.css('zIndex', ++flexdropdownmenu.startzindex)
flexdropdownmenu.positionul($, $flexmenu, e, $target)
flexdropdownmenu.showbox($, $target, $flexmenu, e)
if (flexdropdownmenu.triggerevt=="click")
$target.bind("mouseleave", function(e){
flexdropdownmenu.hidebox($, $target, $flexmenu)

jQuery.fn.addflexmenu=function(flexmenuid, options){
var $=jQuery
return this.each(function(){ //return jQuery obj
var $target=$(this),a;
if (typeof options=="object"){ //if options parameter defined
if (options.dir)
$target.attr('data-dir', options.dir) //set/overwrite data-dir attr with defined value
if (options.offsets)
$target.attr('data-offsets', options.offsets) //set/overwrite data-offsets attr with defined value
if ($('#'+flexmenuid).length==1){ //check flex menu is defined
a=$('#'+flexmenuid); // new
flexdropdownmenu[flexmenuid]={p0:$,p1:$target,p2:a}; // new
flexdropdownmenu.init($, $target, a)

var o=document
o.addEventListener?o.addEventListener('click',function(e){ return flexdropdownmenu.Hide(e); },false):o.attachEvent?o.attachEvent('onclick',function(e){ return flexdropdownmenu.Hide(e); }):null;

//By default, add flex menu to anchor links with attribute "data-flexmenu"
var $anchors=$('*[data-flexmenu]')

//ddlistmenu: Function to define a UL list menu dynamically

function ddlistmenu(id, className){
var menu=document.createElement('ul')
if (id)
if (className)

addItem:function(url, text, target){
var li=document.createElement('li')
li.innerHTML='<a href="'+url+'" target="'+target+'">'+text+'</a>'
return this
var s=new ddlistmenu(null, null)
return s


07-02-2014, 03:18 PM
These lines seems to break the script. The menu doesn't appear when link is clicked now.

var o=document
o.addEventListener?o.addEventListener('click',function(e){ return flexdropdownmenu.Hide(e); },false):o.attachEvent?o.attachEvent('onclick',function(e){ return flexdropdownmenu.Hide(e); }):null;