Is there a way to open a select box using Javascript (and jQuery)?
<select style="width:150px;">
<option value="1">1</option>
<option value="2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc arcu nunc, rhoncus ac dignissim at, rhoncus ac tellus.</option>
<option value="3">3</option>
</select>
I have to open my select, cause of IE bug. All versions of IE (6,7,8) cut my options.
As far as I know, there is no CSS bugfix for this.
At the moment I try to do the following:
var original_width = 0;
var selected_val = false;
if (jQuery.browser.msie) {
$('select').click(function(){
if (selected_val == false){
if(original_width == 0)
original_width = $(this).width();
$(this).css({
'position' : 'absolute',
'width' : 'auto'
});
}else{
$(this).css({
'position' : 'relative',
'width' : original_width
});
selected_val = false;
}
});
$('select').blur(function(){
$(this).css({
'position' : 'relative',
'width' : original_width
});
});
$('select').blur(function(){
$(this).css({
'position' : 'relative',
'width' : original_width
});
});
$('select').change(function(){
$(this).css({
'position' : 'relative',
'width' : original_width
});
});
$('select option').click(function(){
$(this).css({
'position' : 'relative',
'width' : original_width
});
selected_val = true;
});
}
But clicking on my select the first time will change the width of the select but I have to click again to open it.
I know this is pretty old and answered, but this worked for me in Safari and iOS UIWebView - I have it hidden, but want it to show and open when a different button is clicked.
$('#select-id').show().focus().click();
Try this:
var myDropDown=$("#myDropDown");
var length = $('#myDropDown> option').length;
//open dropdown
myDropDown.attr('size',length);
and this to close:
//close dropdown
myDropDown.attr('size',0);
Instead of using click, you could use the mousedown handler to capture the mousedown event.
mousedown fires before click, so you could call stopPropogation to break the event queue.
Try this:
dropDown = function (elementId) {
var dropdown = document.getElementById(elementId);
try {
showDropdown(dropdown);
} catch(e) {
}
return false;
};
showDropdown = function (element) {
var event;
event = document.createEvent('MouseEvents');
event.initMouseEvent('mousedown', true, true, window);
element.dispatchEvent(event);
};
Then call the function:
dropDown('elementId');
NO jQuery solution.
<SCRIPT>
function toggleDropdown(element){
if(element.size == 0) {
element.size = element.length;
element.focus();
}
else element.size = 0;
}
</SCRIPT>
Found this here.
First of all, I feel the pain of this limitation in IE - bleh!
Just thought I'd also share this as it seems to be working for me. I've taken almost the same approach, but on a per select element. In my case I know which lists could have long data.
Instead of making the select elements absolute, I've kept them inline and wrap them in a DIV with a hidden overflow as appearance needed to be consistent, also it only applies this 'hack' if it is IE and the expanded width is greater than the current width.
To use this for all select boxes you could use something like:
$("select").each(function(){
$(this).IELongDropDown();
});
Or obviously on a per element bases by id.
Here's the jquery plugin:
(function($) {
$.fn.IELongDropDown = function(cln) {
if (jQuery.browser.msie) { //only IE has problems with long select boxes
var el = this;
var previousWidth = el.width();
var divWrapper = "<div style='padding:0;margin:0;overflow:hidden;width:"+ previousWidth +"px'></div>";
el.wrap(divWrapper);
var newWidth = el.width("auto").width();
el.width(previousWidth);
if(newWidth > previousWidth) {
el.bind("mousedown", function(){ return el.width("auto").focus(); });
el.bind("blur", function(){ return el.width(previousWidth); });
el.bind("change", function(){ return el.width(previousWidth); });
}
}
return this;
};
})(jQuery);
Hope this helps someone
I think that you need to return true from your event handlers (click, blur, etc.) so after your handler executes, the browser continues to propagate the event and open the select.
It is similar with href links, if they have an onclick handler and the handler returns false, the link is not followed (the browser stops the event after your handler executes).
EDIT: Based on your comment and answer, it seems that your handler gets the first chance to execute only after the browser decides to open the box.
I suggest that you try the focus event handler, it might get a chance to run earlier than the click handler and perhaps before the browser actually opens the box. It is also more consistent (applies both to mouse and keyboard navigation).
I prefer to set my CSS in a CSS file and then "addClass" but even so, your code (portion)
$('select').blur(function(){
$(this).css({
'position' : 'relative',
'width' : original_width
});
});
$('select').blur(function(){
$(this).css({
'position' : 'relative',
'width' : original_width
});
});
seems to be a duplicate
I would make it:
$('select').blur().css({
'position' : 'relative',
'width' : original_width
});
Not sure you really even need the .blur() here what with the .change() event (try taking it out see see if that addresses your issue...I use select often on IE and do not seem to have an issue.
Okay, I found another way fixing this problem.
Here is the fix:
Please give me feedback! I'm kind of proud on myself ;)
$(document).ready(function() {
if (jQuery.browser.msie) {
select_init();
}
});
function select_init () {
var selects = $('select');
for (var i = 0; i < selects.length; i++) {
_resizeselect.init(selects[i]);
}
}
var _resizeselect = {
obj : new Array(),
init : function (el) {
this.obj[el] = new resizeselect (el);
}
}
function resizeselect (el) {
this.el = el;
this.p = el.parentNode;
this.ht = el.parentNode.offsetHeight;
var obj = this;
this.set = false;
el.onmousedown = function () {
obj.set_select("mousedown");
}
el.onblur = function () {
obj.reset_select("blur");
}
el.onchange = function () {
obj.reset_select("change");
}
}
resizeselect.prototype.set_select = function (str) {
if (this.set) {
this.set = false;
return;
}
this.el.style.width = "auto";
this.el.style.position = "absolute";
this.p.style.height = this.ht + "px";
this.p.style.zIndex = 100;
this.set = true;
this.el.focus();
}
resizeselect.prototype.reset_select = function (str) {
this.el.style.width = "";
this.el.style.position = "";
this.p.style.height = "";
this.p.style.zIndex = 1;
this.set = false;
window.focus();
}
The mousedown event is raise before the click event :
first mousedown raise -> set the width to 'auto' (if the state of the dropdown is 'close')
click raise -> store in a var the state of the dropdown : 'open'
We select a value, the second mousedown is raised : nothing to do
click raise -> we need to restore the original width of the dropdown and change the state of the var to : 'close'
The blur and change event are needed to close the dropdown if the user clicked outside the dropdown.
Here the complete solution with Brendan's code :
(function ($) {
var open = {}
$.fn.IELongDropDown = function (cln) {
if (jQuery.browser.msie) { //only IE has problems with long select boxes
var el = this;
var id = el.attr('id');
var margin = 2; //Need to set a margin however the arrow is cut
var previousWidth = el.width() + margin;
var divWrapper = "<div style='padding:0;margin:0;overflow:hidden;width:" + previousWidth + "px'></div>";
el.wrap(divWrapper);
var newWidth = el.width("auto").width();
el.width(previousWidth);
if (newWidth > previousWidth) {
el.mousedown(function () {
if (!open[id]) {
el.width("auto");
el.focus();
}
});
el.click(function () {
if (!open[id])
open[id] = true;
else {
open[id] = false;
return el.width(previousWidth);
}
});
el.blur(function () {
open[id] = false;
return el.width(previousWidth);
});
el.change(function () {
open[id] = false;
return el.width(previousWidth);
});
}
}
return this;
};
})(jQuery);
Call the function :
<script type="text/javascript" language="javascript">
$(document).ready(function () {
$('#mydropdownlist').IELongDropDown();
});
</script>
you can not open the select list but you can do it by changing the size of theselect list on click or any other event you want
$("#drpdwn").mousedown(bodyevent);
function bodyevent()
{
console.log("size changed");
$(this).attr('size',3);
}
$("#drpdwn").focus(function()
{
//alert("txt clicked from ");
var $el = $("#drpdwn");
var offset = $el.offset();
var event = jQuery.Event( "mousedown", {
which: 1,
pageX: offset.left,
pageY: offset.top
});
$el.trigger(event);
});
As an alternative you can use the select2 plugin and do the following:
$(element_select).focus();
$(element_select).select2('open').trigger('open');
It worked perfectly for me in Firefox version 79.0
There is an alternate solution i found for this problem. Just add a theme to your select box like Selectize.js it will convert your select box into ul li html tags but works as select box. You can easily hide show ul li's on jquery events.
Related
I currently have this function:
function highlightBoxes() {
var windowStart = $(window).scrollTop();
var windowEnd = windowStart + $(window).height();
$('.box').each(function() {
var box = $(this);
var start = box.offset().top;
var end = start + box.height();
if (windowStart <= start && windowEnd >= end) {
box.addClass('active');
} else {
box.removeClass('active');
}
});
}
highlightBoxes();
$(document).scroll(highlightBoxes);
$(window).resize(highlightBoxes);
Which checks if an entire element (in this case .box) is in view (jsfiddle). However, I want to be able to use the function as an on event, so I can use it for many different elements. Something like:
$('.box').on('inview', function () {
if (elementIsInView) {
// make the box red
} else {
// make the box the original color
}
});
How can I do this?
using on means you will need to trigger an event with the same name, a super simple version of this is using document as the message bus like:
$(document).trigger('inview');
Therefore at the point in your code where you have decided that inview should be true, fire an event like the above, at which point the on event will run the function.
Base on the code above, you probably want to move the if statement out of the on event, and in fact run that as a separate function. When elementIsInView returns true, you could fire the inview event.
You could use the hover event.
$(document).on('hover','.box', function () {
if (elementIsInView) {
// make the box red
} else {
// make the box the original color
}
});
If am not clear with your answer let me know.
click ,hover ,drag all are events. Event is a action, apply from user. Function is a declare with javascript.Is not directly running.Its only run after event trigger.Event are declared by w3c
I think you need something like that:
Assign function with button click.Its works from button click event.
$('button').on('click',hello)
function hello(){
console.log('button Clicked')
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button >hi</button>
or
$('button').on('click',function hello(){
console.log('button Clicked')
})
For your code do like this
$('.box').on('click', function inview() {
if (elementIsInView) {
// make the box red
} else {
// make the box the original color
}
});
try this:
function checkElementToBeInView(elem, callback) {
$(elem).each(function() {
var element = $(this);
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = element.offset().top;
var elemBottom = elemTop + element.height();
var isInView = ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
callback(element, isInView);
});
}
$(window).on("load resize scroll", function(e) {
checkElementToBeInView(".box", function(elem, isInView) {
if (isInView)
elem.addClass('active');
else
elem.removeClass('active');
});
In the mobile version on my form appears 'Close' button.
Unfortunately, it does not work. I see a normal button, but when I click there is no reaction. In the javascript I load the file form-with-button.html to render form content in my all .html files.
<div id="form-with-button">
</div>
and use javascript to show and close.
//Onload show header and footer content
$("#header").load('header.html', function(){
$('#nav').affix({
offset: {top: $('#header').height()-$('#nav').height()}
});
});
$("#footer").load('footer.html');
$("#contact").load('footer-contact.html');
$("#form-with-button").load('form-with-button.html');
// Sticky Buttons Show Hide
$("#fix-quote, #fix-contact").on("click", function() {
var body = $("body");
var openPos= ["",""];
var id = $(this).attr("id");
var content = $("#"+id+"-content");
var property = (content.attr("class").toString().indexOf("left") > -1)? "left": "right";
if (!body.hasClass("bd_"+id)) {
openPos= [0,380]
var ele = $(this);
body.addClass("bd_"+id)
setTimeout(function(){
body.on("click.fix",function(ev) {
if($(ev.target).closest(".fixed-container").length == 0){
$(ele).click()
}
});
},10);
} else {
body.removeClass("bd_"+id).off("click.fix");
}
content.css("top","").show().css(property, openPos[0]);
$(this).css(property, openPos[1]);
});
// Mobile Requests Showhide
$("#fix-quote-mob, #fix-contact-mob").on("click", function() {
var id = $(this).attr("id").slice(0,-4);
var content = $("#"+id+"-content");
content.show()
setTimeout(function(){
content.css("top",0)
},10)
});
$(".fix-contact-close").on("click", function() {
var content = $(this).closest(".fixed-container");
content.css("top","").delay(400).hide(0);
});
Please help, why my button does not work?
You are trying to add a handler to an element created in a dynamic way, so you need to refactor your code in order to make it work:
$(document).on("click", ".fix-contact-close", function() {
var content = $(this).closest(".fixed-container");
content.css("top","").delay(400).hide(0);
});
I want to make the sticky-nav to act similar(scroll is off when the menu is expanded) to this website's nav(http://amandagerhardsen.com/#cloudbusting/4) when expanded.
How do I do it?
var Boxlayout = (function () {
var $el = $('#sticky-nav'),
$sections = $el.children('section'),
// work panels
$workPanelsContainer = $('#bl-panel-work-items'),
// close work panel trigger
$closeWorkItem = $workPanelsContainer.find('nav > span.hidemenu'),
transEndEventNames = {
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'transitionend',
'OTransition': 'oTransitionEnd',
'msTransition': 'MSTransitionEnd',
'transition': 'transitionend'
},
// transition end event name
transEndEventName = transEndEventNames[Modernizr.prefixed('transition')],
// support css transitions
supportTransitions = Modernizr.csstransitions;
function init() {
initEvents();
}
function initEvents() {
$sections.each(function () {
var $section = $(this);
// expand the clicked section and scale down the others
$section.on('click', function () {
if (!$section.data('open')) {
$section.data('open', true).addClass('bl-expand bl-expand-top');
$el.addClass('bl-expand-item');
}
}).find('span.hidemenu').on('click', function () {
// close the expanded section and scale up the others
$section.data('open', false).removeClass('bl-expand').on(transEndEventName, function (event) {
if (!$(event.target).is('section')) return false;
$(this).off(transEndEventName).removeClass('bl-expand-top');
});
if (!supportTransitions) {
$section.removeClass('bl-expand-top');
}
$el.removeClass('bl-expand-item');
return false;
});
});
// clicking on a work item: the current section scales down and the respective work panel slides up
$workItems.on('click', function (event) {
// scale down main section
$sectionWork.addClass('bl-scale-down');
// show panel for this work item
$workPanelsContainer.addClass('bl-panel-items-show');
var $panel = $workPanelsContainer.find("[data-panel='" + $(this).data('panel') + "']");
currentWorkPanel = $panel.index();
$panel.addClass('bl-show-work');
return false;
});
// navigating the work items: current work panel scales down and the next work panel slides up
$nextWorkItem.on('click', function (event) {
if (isAnimating) {
return false;
}
isAnimating = true;
var $currentPanel = $workPanels.eq(currentWorkPanel);
currentWorkPanel = currentWorkPanel < totalWorkPanels - 1 ? currentWorkPanel + 1 : 0;
var $nextPanel = $workPanels.eq(currentWorkPanel);
$currentPanel.removeClass('bl-show-work').addClass('bl-hide-current-work').on(transEndEventName, function (event) {
if (!$(event.target).is('div')) return false;
$(this).off(transEndEventName).removeClass('bl-hide-current-work');
isAnimating = false;
});
if (!supportTransitions) {
$currentPanel.removeClass('bl-hide-current-work');
isAnimating = false;
}
$nextPanel.addClass('bl-show-work');
return false;
});
// clicking the work panels close button: the current work panel slides down and the section scales up again
$closeWorkItem.on('click', function (event) {
// scale up main section
$sectionWork.removeClass('bl-scale-down');
$workPanelsContainer.removeClass('bl-panel-items-show');
$workPanels.eq(currentWorkPanel).removeClass('bl-show-work');
return false;
});
}
return {
init: init
};
})();
Here is a fiddle: http://jsfiddle.net/77P2e/
Be careful to unlock scrolling again when done, or this could be very annoying for the user!
Setup code
var $window = $(window), previousScrollTop = 0, scrollLock = false;
$window.scroll(function(event) {
if(scrollLock) {
$window.scrollTop(previousScrollTop);
}
previousScrollTop = $window.scrollTop();
});
To lock scroll position:
scrollLock = true;
And to unlock again...
scrollLock = false;
As an example use, you could lock the window scroll position when the mouse enters the navigation area, and unlock it again when the mouse leaves:
$("nav")
.mouseenter(function(){ scrollLock = true; })
.mouseleave(function(){ scrollLock = false; });
In my opinion the accepted answer is not what should be achieved, as the window.scroll() function will be still running (endlessly), even if the 'event' has occured.
The window.scroll() function is an event handler. So use on() to bind the event and off() to unbind it (after the 'event' has occured).
$(window).on('scroll', function() { // bind event handler
var offset = $(window).scrollTop();
console.log("page Y-Offset: ", offset); // just to see it working
if(offset >= 100) $(window).off('scroll'); // unbind the event handler when the condition is met
});
The Javascript solution is a little janky for me, on mobile. It's like it scrolls a little bit and then snaps back into place.
However, I figured out a way to do it much more cleanly, without any jank, just by changing CSS's overflow property on the part you don't want to scroll. Here's the code in d3 but the concept should be pretty clear:
var body = d3.select('body');
var preventScroll = function () {
body.style('overflow', 'hidden');
},
allowScroll = function () {
body.style('overflow', 'scroll');
};
d3.select('#sticky-nav')
.on('touchmove', preventScroll)
.on('touchstart', preventScroll)
.on('touchend', allowScroll)
.on('touchcancel', allowScroll);
As I was using jquery animation,
if ($(window).scrollTop() >= $('.btn').offset().top + $('.btn').outerHeight() - window.innerHeight)
{
$(".tab").stop();
}
I did this and it worked.
.btn is the button. That .tab div would stop if it scrolls to that position.
If you're using jquery animation you can try using the stop() function on the animated object.
In drupal i have generated a list where each item is a fieldset with collapsible, that can contain extra information.
Because of the rather large list i want to avoid loading the extra information until a user clicks on the fieldset.
Best case scenario:
User clicks on collapsed fieldset.
Fieldset loads extra information.
Fieldset uncollapses.
I've copied and loaded the copy of collapse.js into my form, but I'm very new to js and jQuery, so I'm a little lost. If someone can show me how to call a function the first time the fieldset is expanded, I'm sure i can figure out the rest.
I've included the code from collapse.js:
(function ($) {
//Toggle the visibility of a fieldset using smooth animations.
Drupal.toggleFieldset = function (fieldset) {
var $fieldset = $(fieldset);
if ($fieldset.is('.collapsed')) {
var $content = $('> .fieldset-wrapper', fieldset).hide();
$fieldset
.removeClass('collapsed')
.trigger({ type: 'collapsed', value: false })
.find('> legend span.fieldset-legend-prefix').html(Drupal.t('Hide'));
$content.slideDown({
duration: 'fast',
easing: 'linear',
complete: function () {
Drupal.collapseScrollIntoView(fieldset);
fieldset.animating = false;
},
step: function () {
// Scroll the fieldset into view.
Drupal.collapseScrollIntoView(fieldset);
}
});
}
else {
$fieldset.trigger({ type: 'collapsed', value: true });
$('> .fieldset-wrapper', fieldset).slideUp('fast', function () {
$fieldset
.addClass('collapsed')
.find('> legend span.fieldset-legend-prefix').html(Drupal.t('Show'));
fieldset.animating = false;
});
}
};
//Scroll a given fieldset into view as much as possible.
Drupal.collapseScrollIntoView = function (node) {
var h = document.documentElement.clientHeight || document.body.clientHeight || 0;
var offset = document.documentElement.scrollTop || document.body.scrollTop || 0;
var posY = $(node).offset().top;
var fudge = 55;
if (posY + node.offsetHeight + fudge > h + offset) {
if (node.offsetHeight > h) {
window.scrollTo(0, posY);
}
else {
window.scrollTo(0, posY + node.offsetHeight - h + fudge);
}
}
};
Drupal.behaviors.collapse = {
attach: function (context, settings) {
$('fieldset.collapsible', context).once('collapse', function () {
var $fieldset = $(this);
// Expand fieldset if there are errors inside, or if it contains an
// element that is targeted by the uri fragment identifier.
var anchor = location.hash && location.hash != '#' ? ', ' + location.hash : '';
if ($('.error' + anchor, $fieldset).length) {
$fieldset.removeClass('collapsed');
}
var summary = $('<span class="summary"></span>');
$fieldset.
bind('summaryUpdated', function () {
var text = $.trim($fieldset.drupalGetSummary());
summary.html(text ? ' (' + text + ')' : '');
})
.trigger('summaryUpdated');
// Turn the legend into a clickable link, but retain span.fieldset-legend
// for CSS positioning.
var $legend = $('> legend .fieldset-legend', this);
$('<span class="fieldset-legend-prefix element-invisible"></span>')
.append($fieldset.hasClass('collapsed') ? Drupal.t('Show') : Drupal.t('Hide'))
.prependTo($legend)
.after(' ');
// .wrapInner() does not retain bound events.
var $link = $('<a class="fieldset-title" href="#"></a>')
.prepend($legend.contents())
.appendTo($legend)
.click(function () {
var fieldset = $fieldset.get(0);
// Don't animate multiple times.
if (!fieldset.animating) {
fieldset.animating = true;
Drupal.toggleFieldset(fieldset);
}
return false;
});
$legend.append(summary);
});
}
};
})(jQuery);
It looks to me like you'd have to override the whole Drupal.toggleFieldset function (just like when you are overriding a Drupal theme function.
You could perhaps add a class to the fieldset in FormAPI then catch it in the complete function of the $content.slideDown params and fire a custom function of yours, to add a 'loading' graphic and make your ajax request.
I'm assuming from your question that you are familiar enough with FormAPI/jQuery.ajax() to have a go. But let me know if not and i'll include some snippets
EDIT
Here is some example code, it'd take a quite a while to setup a test environment for this, so it'just a pointer (cant create a JS fiddle for this ;))
You might add your fieldset like this in PHP
$form['my_fieldset'] = array(
'#type' = 'fieldset',
'#title' = t('My fieldset'),
'#collapsible' = true,
'#collapsed' = true,
'#attributes' => array(
'class' => array('ajax-fieldset'),
'rel' => 'callback/url/path' // random attribute to store the link to a menu path that will return your HTML
)
);
$form['my_fieldset'] = array(
'#markup' => '<div class="response">loading...</div>'
);
You'll also obviously have setup a menu hook returning your themed data # callback/url/path. IMO it's better to return JSON data and theme them in with JS templating, but the Drupal way (for the moment at least) seems to be to render HTML in the menu hook callback function.
Then here is the JS. I've only included the altered complete function, rather than reproduce what you pasted. Add the complete function in to a copy of the code the re-specify the core Drupal function in your own JS file
$content.slideDown({
complete: function () {
Drupal.collapseScrollIntoView(fieldset);
fieldset.animating = false;
if($fieldset.hasClass('ajax-fieldset')) {
$.get(
Drupal.settings.basePath + $fielset.attr('rel'),
function(data) {
$fieldset.find('.response').html(data);
}
)
}
}
});
Or, rather than messing around with the collapsible function. just create your own fieldset without the collapsible/collapsed classes and implement from scratch yourself
....so.. something like that :)
I have a two containers -- one is nested inside of another. When I hover over the parent, I want the child container to appear. When I mouseout, I want the child container to fadeout. The problem I'm having is the child container has a form that contains a "select box". When the user selects the select box -- the mouseleave event is accidentally fired.
How can I stop the select box from tripping the mouseleave event?
You can see my working code here: http://jsfiddle.net/rsturim/9TZyh/3/
Here's a summary of my script:
$('#parent-container').live("mouseenter", function () {
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function (e) {
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().hide();
});
edit: appears fine in WebKit-based browsers. Fails in Firefox and IE7-IE9.
In most cases you should simply be able to check to see if the event target was a select element, and only continue in the case that it wasn't. Seems much cleaner than the accepted solution, and worked well in my case.
I've modified the fiddle: http://jsfiddle.net/Dygerati/uj3ZC/5/
$('#parent-container').live("mouseenter", function() {
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function(e) {
if(e.target.tagName.toLowerCase() != "select") {
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().hide();
}
});
$('#parent-container').live("mouseenter", function () {
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function (e) {
/* Solution */
if(e.relatedTarget == null) return;
/************/
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().hide();
});
Since mouseleave and mouseenter events are non-standard you can get such lags here and there. The only method I can suggest to fix that is using some hacks. Here is http://jsfiddle.net/mPDcu/1/ improved version of you code.
var selectOpened = false;
$('#select-grind-type').click(function(e){
selectOpened = !selectOpened;
e.stopPropagation();
});
$('body').click(function(){
if (selectOpened) {
selectOpened = false;
}
})
$('#parent-container').on("mouseenter", function() {
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave", function(e) {
if (!selectOpened) {
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().hide();
}
});
I had the same problem in a project in which I am contributing, and the other answers didn't work fine for me. My tricky solution was to check if the mouse position inside the event object is inside the parent container. Works pretty good!
var layer = $('#parent-container'),
layerPos = {};
$(layer).mouseenter(function () {
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().fadeTo('slow', 1.0, function(){
layerPos.x = {
min: $(layer).offset().left,
max: $(layer).offset().left + $(layer).width()
};
layerPos.y = {
min: $(layer).offset().top,
max: $(layer).offset().top + $(layer).height()
};
});
})
$(layer).mouseleave(function(e) {
// check if event mouse position is inside parent container
var x_con = e.pageX >= layerPos.x.min && e.pageX <= layerPos.x.max;
var y_con = e.pageY >= layerPos.y.min && e.pageY <= layerPos.y.max;
if ( x_con && y_con ) {
return false;
}
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().hide();
});
You can also check the navigator version to avoid this code to execute in browsers that support this functionality like Chrome.
This partly solves the problem.
Unbind the mouseleave event when the select box gains focus and bind again when it loses focus.
http://jsfiddle.net/9TZyh/5/
$('#parent-container').live("mouseenter", function() {
var $this = $(this);
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().fadeTo('slow', 1.0);
}).live("mouseleave",focusOut);
$("#select-grind-type").live("focus",function(){
$('#parent-container').die("mouseleave");
});
$("#select-grind-type").live("focusout change",function(){
$('#parent-container').live("mouseleave",focusOut);
});
function focusOut(e) {
var $this = $(this),
$selectOptionsContainer = $this.find('#child-container');
$selectOptionsContainer.stop().hide();
}
If you don't mind having the fade not working in some old browsers, you could do it with CSS quickly:
#parent-container { }
#child-container {
opacity:0;
-webkit-transition:opacity 1s ease-in;
-moz-transition:opacity 1s ease-in;
}
#parent-container:hover #child-container {{
opacity:1;
-webkit-transition:opacity 1s ease-out;
-moz-transition:opacity 1s ease-out;
}
Those guys give you a working alternative, but it also has some bugs. For example, if you quit the outer box while the combobox is still opened, it won't fade out. I recommend you a much easier alternative that will also fix that bug.
Instead of thinking in the mouseleave event of the inner box, why don't you swap your mind to think in the other way around? I mean, leaving the inner box, also means entering in another container. So you can do outerContainer.mouseenter(function(){ hideInnerBox() }); :-)
Obviously for that purpose innerbox should not be a child of outerbox, even if visually it seems so (css positioning can be used to achieve it)
So I just ran into a similar issue with a <select> nested in a container and came across this question. Here's what I ended up doing.
$("#container").mouseover(function(e){
var t = $(this);
t.addClass('active');
var offset = t.offset();
var xMin = offset.left;
var yMin = offset.top;
var xMax = xMin + t.innerWidth();
var yMax = yMin + t.innerHeight();
t.parent().mousemove(function(e){
if(e.pageX < xMin || e.pageX > xMax-2 || e.pageY < yMin || e.pageY > yMax ){
t.removeClass('active');
// unbind this event
$(this).unbind('mousemove');
}
});
});
Basically, when you mouseover the container, we collect its bounds and start checking whether or not the mouse is over the element. When we know the mouse is gone, we unbind the mousemove listener.
I'd make a jsfiddle for you but its running so slow today!
Hope that helps.
You should only check if the current Element is a descendant of your container.
If so abort the handler.
See: jquery descendant
Example:
ContainerElement.mouseleave(function (e) {
if (ContainerElement.has(e.fromElement).length > 0) return;
// Do your Stuff
});