How to close div when div loses focus? - javascript

I made a simple plunkr here http://plnkr.co/edit/zNb65ErYH5HXgAQPOSM0?p=preview
I created a little datepicker I would like this to close itself when you focus out of it (focusout of datepicker) if I put blur on input I'm unable to use the datepicker, if I put focusout event on datepicker it doesn't works
I also tried:
angular.element(theCalendar).bind('blur', function () {
$scope.hideCalendar();
});
but it doesn't work.
Any clue?

this is because you are removing the item before you get a chance to do anything, here is a working example:
http://plnkr.co/edit/mDfV9NLAQCP4l7wHdlfi?p=preview
just add a timeout:
thisInput.bind('blur', function () {
$timeout(function(){
$scope.hideCalendar();
}, 200);
});
have you considered using existing datepickers? like angularUI or angular-strap: http://mgcrea.github.io/angular-strap/##datepickers
Update:
Not a complete solution, but should get you quite closer:
angular.element($document[0].body).bind('click', function(e){
console.log(angular.element(e.target), e.target.nodeName)
var classNamed = angular.element(e.target).attr('class');
var inThing = (classNamed.indexOf('datepicker-calendar') > -1);
if (inThing || e.target.nodeName === "INPUT") {
console.log('in');
} else {
console.log('out');
$timeout(function(){
$scope.hideCalendar();
}, 200);
}
});
http://plnkr.co/edit/EbQl5xsCnG837rAEhBZh?p=preview
What you want to do then is to listen for a click on the page, and if the click is outside of the calendar, then close it, otherwise do nothing. The above only takes into account that you are clicking on something that has a class name which includes datepicker-calendar, you will need to adjust it so that clicking within the calendar doesn't close it as well.

How about closing on mouseout?
You need to cancel the close if you move to another div in the calendar though:
//get the calendar as element
theCalendar = element[0].children[1];
// hide the calendar on mouseout
var closeCalendarTimeout = null;
angular.element(theCalendar).bind('mouseout', function () {
if ( closeCalendarTimeout !== null )
$timeout.cancel(closeCalendarTimeout);
closeCalendarTimeout = $timeout(function () {
$scope.hideCalendar();
},250)
});
angular.element(theCalendar).bind('mouseover', function () {
if ( closeCalendarTimeout === null ) return
$timeout.cancel(closeCalendarTimeout);
closeCalendarTimeout = null;
});
EDIT
Adding a tabindex attribute to a div causes it to fire focus and blur events.
, htmlTemplate = '<div class="datepicker-calendar" tabindex="0">' +
angular.element(theCalendar).bind('blur', function () {
$scope.hideCalendar();
});

So, i know it probably is not the best practice or the best way to do this, but at the end i fixed and got what i need using this:
thisInput.bind('focus click', function bindingFunction() {
isMouseOnInput = true;
$scope.showCalendar();
angular.element(theCalendar).triggerHandler('focus');
});
thisInput.bind('blur focusout', function bindingFunction() {
isMouseOnInput = false;
});
angular.element(theCalendar).bind('mouseenter', function () {
isMouseOn = true;
});
angular.element(theCalendar).bind('mouseleave', function () {
isMouseOn = false;
});
angular.element($window).bind('click', function () {
if (!isMouseOn && !isMouseOnInput) {
$scope.hideCalendar();
}
});
I setted up some boolean vars to check where mouse is when you click the page and it works like a charm if you have some better solution that works , please let me know, but this actually fixed all.
I accept this as the answer but i thank all the guys on this page!

Related

Is there a way to call jquery functions on another object after finishing the first?

I want to fade out object 1 and after fade out remove a class and add one. After that on another object the object 2 should fadein and then i assign it a class. The problem that i encountered is that if i fire my event faster than the fadein/fadeout the object stays active.
$('.menuA').on("click", function () {
$('.menuA').removeClass("blue accent-3 z-depth-2", 100);
let clicked = $(this);
$('.menuA').promise().done(function () {
clicked.addClass("blue accent-3 z-depth-2", 100);
})
animatePanes($(this).attr("con"));
})
function animatePanes(pane) {
let paneOld = $('.pane-active');
paneOld.fadeOut(250).removeClass("pane-active").addClass("pane-inactive").promise().done(function () {
$('.' + pane).fadeIn(250).removeClass("pane-inactive").addClass("pane-active");
});
};
Thats my event with the function. The problem is that if i click to fast and trigger the event on menuA the paneOld doesnt get the class pane-inactive.
I already tried to do a global variable that checks if the event is running but it didnt worked (probably because i thinked wrong).
Is there a way to disable the event listener until the event is completly finished?
Or is there a better way?
You can surely do it with a global variable - as you've already said. Maybe you just put in in the wrong place.
I'd recommend something like isAnimating=false and 'disable' the click event listener if it's value is true. This way you can reset isAnimating to false as soon as all your animations are completed.
var isAnimating = false;
$('.menuA').on("click", function() {
if (!isAnimating) {
$('.menuA').removeClass("blue accent-3 z-depth-2", 100);
let clicked = $(this);
$('.menuA').promise().done(function() {
clicked.addClass("blue accent-3 z-depth-2", 100);
})
animatePanes($(this).attr("con"));
isAnimating = true;
}
})
function animatePanes(pane) {
let paneOld = $('.pane-active');
paneOld.fadeOut(250).removeClass("pane-active").addClass("pane-inactive").promise().done(function() {
$('.' + pane).fadeIn(250).removeClass("pane-inactive").addClass("pane-active").promise().done(function() {
isAnimating = false;
});
});
}

Prevent click after focus event

When user clicks on input field, two consecutive events are being executed: focus and click.
focus always gets executed first and shows the notice. But click which runs immediately after focus hides the notice. I only have this problem when input field is not focused and both events get executed consecutively.
I'm looking for the clean solution which can help me to implement such functionality (without any timeouts or weird hacks).
HTML:
<label for="example">Example input: </label>
<input type="text" id="example" name="example" />
<p id="notice" class="hide">This text could show when focus, hide when blur and toggle show/hide when click.</p>
JavaScript:
$('#example').on('focus', _onFocus)
.on('blur', _onBlur)
.on('click', _onClick);
function _onFocus(e) {
console.log('focus');
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
$('#notice').removeClass('hide');
}
function _onClick(e) {
console.log('click');
$('#notice').toggleClass('hide');
}
function _onBlur(e) {
console.log('blur');
$('#notice').addClass('hide');
}
UPDATED Fiddle is here:
I think you jumbled up the toggles. No need to prevent propagation and all that. Just check if the notice is already visible when click fires.
Demo: http://jsfiddle.net/3Bev4/13/
Code:
var $notice = $('#notice'); // cache the notice
function _onFocus(e) {
console.log('focus');
$notice.removeClass('hide'); // on focus show it
}
function _onClick(e) {
console.log('click');
if ($notice.is('hidden')) { // on click check if already visible
$notice.removeClass('hide'); // if not then show it
}
}
function _onBlur(e) {
console.log('blur');
$notice.addClass('hide'); // on blur hide it
}
Hope that helps.
Update: based on OP's clarification on click toggling:
Just cache the focus event in a state variable and then based on the state either show the notice or toggle the class.
Demo 2: http://jsfiddle.net/3Bev4/19/
Updated code:
var $notice = $('#notice'), isfocus = false;
function _onFocus(e) {
isFocus = true; // cache the state of focus
$notice.removeClass('hide');
}
function _onClick(e) {
if (isFocus) { // if focus was fired, show/hide based on visibility
if ($notice.is('hidden')) { $notice.removeClass('hide'); }
isFocus = false; // reset the cached state for future
} else {
$notice.toggleClass('hide'); // toggle if there is only click while focussed
}
}
Update 2: based on OP's observation on first click after tab focus:
On second thought, can you just bind the mousedown or mouseup instead of click? That will not fire the focus.
Demo 3: http://jsfiddle.net/3Bev4/24/
Updated code:
$('#example').on('focus', _onFocus)
.on('blur', _onBlur)
.on('mousedown', _onClick);
var $notice = $('#notice');
function _onFocus(e) { $notice.removeClass('hide'); }
function _onClick(e) { $notice.toggleClass('hide'); }
function _onBlur(e) { $notice.addClass('hide'); }
Does that work for you?
Setting a variable for "focus" seems to do the trick : http://jsfiddle.net/3Bev4/9/
Javascript:
$('#example').on('focus', _onFocus)
.on('click', _onClick)
.on('blur', _onBlur);
focus = false;
function _onFocus(e) {
console.log('focus');
$('#notice').removeClass('hide');
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
focus = true;
}
function _onClick(e) {
console.log('click');
if (!focus) {
$('#notice').toggleClass('hide');
} else {
focus = false;
}
}
function _onBlur(e) {
console.log('blur');
$('#notice').addClass('hide');
}
If you want to hide the notice onBlur, surely it needs to be:
function _onBlur(e) {
console.log('blur');
$('#notice').addClass('hide'); // Add the hidden class, not remove it
}
When doing this in the fiddle, it seemed to fix it.
The code you have written is correct, except that you have to replae $('#notice').removeClass('hide'); with $('#notice').addClass('hide');
Because onBlur you want to hide so add hide class, instead you are removing the "hide" calss.
I hope this is what the mistake you have done.
Correct if I am wrong, Because I don't know JQuery much, I just know JavaScript.
you can use many jQuery methods rather than add or move class:
Update: add a params to deal with the click function
http://jsfiddle.net/3Bev4/23/
var showNotice = false;
$('#example').focus(function(){
$('#notice').show();
showNotice = true;
}).click(function(){
if(showNotice){
$('#notice').show();
showNotice = false;
}else{
showNotice = true;
$('#notice').hide();
}
}).blur(function(){
$('#notice').hide();
});

jQuery accordion plugin (like Facebook or Google)

I have seen many scripts for accordion on the internet, but haven't found anything which meets my needs. I want to make a plugin for accordion just like that which Facebook uses. It should work like this:
Click to open and close.
Add style class while open.
Close upon an outside click. It should not close when click event is inside the accordion box.
Is there any way to achieve this? Please help if you know. I am new to jQuery...
Thanks in advance.
No need for a plug-in when 15 lines of code will solve it: it's about changing the order of the click events that trigger on a specific DOM element and on the window using setTimeout.
var ShowingAccordion = false;
$(document).ready(function () {
$('.MyAccordionOpener').click(function () {
if ($(this).next('.MyAccordion').is(':visible') === false) {
ShowingAccordion = false;
$('.MyAccordion').hide(500);
$(this).next('.MyAccordion').addClass('SomeClass');
$(this).next('.MyAccordion').show(500);
setTimeout(function () { ShowingAccordion = true; }, 1);
}
});
$('.MyAccordion').click(function () {
ShowingAccordion = false;
//this is the line that solves your problem
setTimeout(function () { ShowingAccordion = true; }, 1);
});
$(document).click(function () {
if (ShowingAccordion === true) {
$('.MyAccordion').hide(500);
ShowingAccordion = false;
}
});
});
And here's the demo
If it does what you need then you can accept the answer and happy coding!

focus() doesn't work inside colorbox pop-up

I tried to use focus for first input field on the form. but
it doesn't work. When I call attr("id") for that input it worked. When I call focus for the same input, I didn't see any
result. I also tried to use native Javascript. Does anyone know how to
fix that?
You are all misunderstanding the question. When Colorbox opens you can't focus an input field?
...unless you add your focus to the Colobox onComplete key e.g.
$('#mydiv a').colorbox({ onComplete:function(){ $('form input:first').focus(); }});
You could also bind the focus to an event hook:
$('#mydiv a').bind('cbox_complete', function(){
$('form input:first').focus();
});
That should be enough to get started.
use
$(document).ready(function() {
// focus on the first text input field in the first field on the page
$("input[type='text']:first", document.forms[0]).focus();
});
It may be happening that when your colorbox is opened its focus goes onto the highest element i.e. body of page. use document.activeElement to find that focus went to which element. Then find iframe or id of your colorbox and then set focus on it
Try the first selector,
$("form input:first").focus();
http://jsfiddle.net/erick/mMuFc/
I've just stumbled on this problem.
I think it's best to have a single $.colorbox opener like this:
function showActionForColorBox(
_url,
_forFocus
) {
$.colorbox(
{
scrolling: false,
href: _url,
onComplete: function () {
idColorboxAjaxIndect1.appendTo($('#cboxOverlay'));
idColorboxAjaxIndect2.appendTo($('#cboxOverlay'));
idColorboxAjaxIndect3.appendTo($('#cboxOverlay'));
idColorboxAjaxIndect4.appendTo($('#cboxOverlay'));
// --> Possible element's ID for focus
if (_forFocus) {
$('#' + _forFocus).focus();
}
return;
},
onCleanup: function () {
// TODO: ?
return;
},
onClosed: function () {
if (shouldReloadPageAfterColorBoxAction) {
// --> Should we reload whole page?
shouldReloadPageAfterColorBoxAction = false; // NOTE: To be sure: Reset.
window.location.reload(false);
}
else if (cbEBillsActionReloadPopup) {
// --> Should we reload colorbox
cbEBillsActionReloadPopup = false;
showActionForColorBox(_url);
}
else if (cbShouldLoadAnotherContentAfterClosed) {
// --> Should we reload colorbox with custom content?
cbShouldLoadAnotherContentAfterClosed = false;
$.colorbox({ html: setupContentForcbShouldLoadAnotherContentAfterClosed });
setupContentForcbShouldLoadAnotherContentAfterClosed = '';
}
return;
}
}
);
return;
}
You can also use
$.colorbox({
...,
trapFocus: false
});
to disable focus inside colorbox

How to call a function with jQuery blur UNLESS clicking on a link?

I have a small jQuery script:
$('.field').blur(function() {
$(this).next().children().hide();
});
The children that is hidden contains some links. This makes it impossible to click the links (because they get hidden). What is an appropriate solution to this?
This is as close as I have got:
$('.field').blur(function() {
$('*').not('.adress').click(function(e) {
foo = $(this).data('events').click;
if(foo.length <= 1) {
// $(this).next('.spacer').children().removeClass("visible");
}
$(this).unbind(e);
});
});
The uncommented line is suppose to refer to the field that is blurred, but it doesn't seem to work. Any suggestions?
You can give it a slight delay, like this:
$('.field').blur(function() {
var kids = $(this).next().children();
setTimeout(function() { kids.hide(); }, 10);
});
This gives you time to click before those child links go away.
This is how I ended up doing it:
var curFocus;
$(document).delegate('*','mousedown', function(){
if ((this != curFocus) && // don't bother if this was the previous active element
($(curFocus).is('.field')) && // if it was a .field that was blurred
!($(this).is('.adress'))
) {
$('.' + $(curFocus).attr("id")).removeClass("visible"); // take action based on the blurred element
}
curFocus = this; // log the newly focussed element for the next event
});
I believe you can use .not('a') in this situation:
$('.field').not('a').blur(function() {
$(this).next().children().hide();
});
This isn't tested, so I am not sure if this will work or not.

Categories