How to trigger focusout event on a ul element? - javascript

I have a plugin that changes the look of select html tag on all browser.
I'm trying to make the new styled set of elements behave like a normal select tag. I'm almost there, but I only need to figure one thing out and that's how to hide a ul on focus out.
First off, here is a demo of the new select element (not in English, but you can find it easily ^^) :
http://mahersalam.co.cc/projects/n-beta/
If you click the toggle button of the select element, and then click away, the ul element that has the options won't disappear. That's because I can't fire a focusout event on that ul.
Here is the code that controls how the events are handled:
// Make a div which will be used offline and then inserted to the DOM
$namodgSelect = $('<div class="namodg-select"></div>');
/* other stuff ... */
$namodgSelect // Handle all needed events from the wrapper
.delegate('a', 'click focus blur', function(e) {
// Type of the event
var type = e.type,
// Declare other vars
id,
$this;
e.preventDefault(); // Stop default action
// Make an id ot the element using it's tag name and it's class name
// Note: Because we add a class on the active toggler, it's easier to remove it from here and the logic will still work
id = e.target.tagName.toLowerCase() + '.' + e.target.className.replace(' toggler-active', '');
switch (id) {
case 'p.selected': case 'div.toggle-button':
// Only accept 'click' on p and div
if ( type != 'click') {
return;
}
// Show and hide the options holder
if ( $optionsHolder.data('hidden') ) {
$selectElem.focus();
// This needs to run fast to give feedback to the user
$toggler.addClass('toggler-active').data('active', true);
// Show the options div
$optionsHolder.stop(true, true).slideDown('fast', function() {
// Sometimes fast clicking makes the toggler deavtive, so show it in that case
if ( ! $toggler.data('active') ) {
$toggler.addClass('toggler-active').data('active', true);
}
}).data('hidden', false);
} else {
$selectElem.blur();
// Hide the options div
$optionsHolder.stop(true, true).slideUp(function() {
// Only hide the toggler if it's active
if ( $toggler.data('active') ) {
$toggler.removeClass('toggler-active').data('active', false);
}
}).data('hidden', true);
}
break;
case 'a.toggler':
switch (type) {
case 'focusin':
$selectElem.focus();
break;
case 'focusout':
// Only blur when the options div is deactive
if ( $optionsHolder.data('hidden') ) {
$selectElem.blur();
}
break;
case 'click':
$selectedHolder.click();
$selectElem.focus();
}
break;
case 'a.option':
// Stop accept click events
if ( type != 'click') {
return;
}
// cache this element
$this = $(this);
// Change the value of the selected option and trigger a change event
$selectedOption.val( $this.data('value') ).change();
// Change the text of the fake select and trigger a click on it
$selectedHolder.text( $this.text() ).click();
break;
}
})
The whole code can be seen from the demo. As you can see, I already use the focusout event on the toggler and the options, so I can't hide the ul when that happens (that will disable the tab functionality).
If anyone can help me with this , it will be appreciated. Thanks.

Try out the jQuery outside events plugin
Will let you do something like:
$(this).bind( "clickoutside", function(event){
$(this).hide();
});

I was able to hide the options panel using this code:
$(document).click(function() {
if ( $optionsHolder.data('hidden') || $optionsHolder.is(':animated') ) {
return;
}
$selectedHolder.click();
})
This works because focusing on another input is like a click on the document.

Related

How do we detect a double click outside an element?

I have this function:
this.div.click( function(e) {
...
});
I would like to listen for double clicks outside this element. I know that we can use blur() for clicks outside an element. But I would like to handle only double click events. What's the best way to do this?
You can use the .dblclick() event to listen to the double-click at the body level, and then use it's target attribute and .contains() to see if the click occurred within the div.
Something like this:
// div to check if dbl click did _not_ originate from
var mydiv = jQuery("#mydiv").get(0);
// listen to body for double clicks
$("body").dblclick(function(e) {
// if click target does not fall within #mydiv
if (mydiv !== e.target && $.contains(mydiv, e.target) !== true) {
console.log("outside of mydiv");
}
});
Here is a jsbin demo.
There is another way to do this, by modifying e.originalEvent:
$( "#mydiv" ).dblclick(function(e) {
e.originalEvent.inside = true;
});
$( "body" ).dblclick(function(e) {
if( e.originalEvent.inside ) {
console.log('inside');
} else {
console.log('outside');
};
});
I have updated Johnatan's Bin. Think it should be faster.

Chaining multiple Jquery events with OR operator

I'm creating a panel that slides down when the user focuses the search box.
I'm terrible at Jquery but still learning, I've managed to create the basic functionality:
$(document).ready(function() {
$(".search-panel").hide();
$("#search_form [type='text']")
.focus(function() {
$(".search-panel").slideDown("fast");
})
.focusout(function() {
$(".search-panel").slideUp("fast");
});
});
with this basic functionality clicking outside the text box will fold up the panel I'm trying to implement a complex set of conditions whereby:
IF (textbox.focus) { show search panel}
IF (texbox.losefocus) && ( NOT search-panel.mouseover)
&& ( NOT (anything-in-search-panel-is-focused) )
basically I need to make sure that the user is not hovering over or interacting with the panel in some way and that the textbox is not focused before I slide it up.
JsFiddle of current situation:
http://jsfiddle.net/b9g9d6gf/
Instead of using the .focusout() function, you should bind a click function on the document.
$(document).ready(function () {
$(".search-panel").hide();
$("#search_form [type='text']")
.focus(function () {
$(".search-panel").slideDown("fast");
});
$(document).click(function(e) {
if( !( $(e.target).is('#search_form *')) ){
$(".search-panel").slideUp("fast");
}
});
});
If the document is clicked, anywhere, it looks if the target isn't a element inside #search_form. If not, it will slide up the .search-panel.
Note:
I removed the label and changed the span to labels. Clicking a label will also (un)check the checkbox inside it. Having three checkboxes making it act wrong. So either make three separate labels (instead of span) or remove it.
Updated Fiddle
Try this Working Demo
<script>
$(document).mouseup(function (e)
{
var container = $("#search_form");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
$(".search-panel").slideUp("fast");
}
else
{
$(".search-panel").slideDown("fast");
$("#search_form [type='text']").focus();
}
});
</script>

Jquery disabled an event, and re activate it later

1 - I've gat an html tag with data-needlogged attribute.
2 - I would like to disable all click events on it.
3 - When the user click on my element, I want to display the authentification popin.
4 - When the user will be logged, I would like to launch the event than I disabled before.
I try something like the following code but it miss the "...?" part.
Play
<script>
// 1 - some click events has been plug on the tag.
jQuery('[data-btnplay]').on('click', function() {
alert('play');
return false;
});
// 2 - disabled all click events
jQuery('[data-needlogged]').off('click');
// 3 - Add the click event to display the identification popin
var previousElementClicked = false;
jQuery('body').on('click.needlogged', '[data-needlogged]="true"', function() {
previousElementClicked = jQuery(this);
alert('show the identification popin');
return false;
});
jQuery(document).on('loginSuccess', function() {
// 4 - on loginSuccess, I need to remove the "the show the identification popin" event. So, set the data-needlogged to false
jQuery('[data-needlogged]')
.data('needlogged', 'false')
.attr('data-needlogged', 'false');
// 4 - enable the the initial clicks event than we disabled before (see point 2) and execute then.
// ...?
jQuery('[data-needlogged]').on('click'); // It doesn't work
if (previousElementClicked) {
previousElementClicked.get(0).click();
}
});
</script>
Thanks for your help
Thank for your answer.
It doesn't answer to my problem.
I will try to explain better.
When I declare the click event on needlogged element, I don't know if there is already others click event on it. So, in your example how you replace the alert('play'); by the initial event ?
I need to find a way to
1 - disable all click events on an element.
2 - add a click event on the same element
3 - and when a trigger is launch, execute the events than I disabled before.
So, I found the solution on this stackoverflow
In my case, I don't realy need to disable and enable some event but I need to set a click event before the other.
Play
<script>
// 1 - some click events has been plug on the tag.
jQuery('[data-btnplay]').on('click', function() {
alert('play');
return false;
});
// [name] is the name of the event "click", "mouseover", ..
// same as you'd pass it to bind()
// [fn] is the handler function
jQuery.fn.bindFirst = function(name, fn) {
// bind as you normally would
// don't want to miss out on any jQuery magic
this.on(name, fn);
// Thanks to a comment by #Martin, adding support for
// namespaced events too.
this.each(function() {
var handlers = $._data(this, 'events')[name.split('.')[0]];
// take out the handler we just inserted from the end
var handler = handlers.pop();
// move it at the beginning
handlers.splice(0, 0, handler);
});
};
var previousElementClicked = false;
// set the needlogged as first click event
jQuery('[data-needlogged]').bindFirst('click', function(event) {
//if the user is logged, execute the other click event
if (userIsConnected()) {
return true;
}
//save the click element into a variable to execute it after login success
previousElementClicked = jQuery(this);
//show sreenset
jQuery(document).trigger('show-identification-popin');
//stop all other event
event.stopImmediatePropagation();
return false;
});
jQuery(document).on('loginSuccess', function() {
if (userIsConnected() && lastClickedElement && lastClickedElement.get(0)) {
// if the user has connected with success, execute the click on the element who has been save before
lastClickedElement.get(0).click();
}
});

How to bind an event to Tabbing Off an element?

I'm using the following for a dropdown:
/* recurse through dropdown menus */
$('.dropdown').each(function() {
/* track elements: menu, parent */
var dropdown = $(this);
var menu = dropdown.next('div.dropdown-menu'), parent = dropdown.parent();
/* function that shows THIS menu */
var showMenu = function() {
hideMenu();
showingDropdown = dropdown.addClass('dropdown-active');
showingMenu = menu.show();
showingParent = parent;
};
/* function to show menu when clicked */
dropdown.bind('click',function(e) {
if(e) e.stopPropagation();
if(e) e.preventDefault();
showMenu();
});
/* function to show menu when someone tabs to the box */
dropdown.bind('focus',function() {
showMenu();
});
});
/* hide when clicked outside */
$(document.body).bind('click',function(e) {
if(showingParent) {
var parentElement = showingParent[0];
if(!$.contains(parentElement,e.target) || !parentElement == e.target) {
hideMenu();
}
}
});
Notice:
$(document.body).bind('click',function(e) {
The problem is that the dropdown opens on click or when you tab to it. But it only closes on click, not when you tab out.
How can I bind an event to mean "tabbing out" ,losing focus, so the dropdown closes?
Thanks
You could trigger a click outside when you press the tab key. Like this:
$('#your_dropdown').bind('keydown', function(e) {
var keyCode = e.keyCode || e.which;
if (keyCode == 9) { //If it's the tab key
$("body").click(); //Force a click outside the dropdown, so it forces a close
}
});
Hope this helps. Cheers
Try the blur event, which will be triggered when the control loses focus (i.e., when the user clicks outside the control or uses the keyboard to tab to the next control).
Put something like this just after your existing focus binding:
dropdown.bind('blur',function() {
// whatever tests you want
hideMenu();
});
(You shouldn't then need the separate click binding that you're currently using to hide the menu.)
P.S. You might also consider focusout, which is similar to blur except that it bubbles.
Seems like you're looking for the onblur event?

How to get Javascript event to fire only when the background is clicked (and not other elements)?

I'm trying to write a web app which replaces the context menu (right-click menu) with my own customized ones. I want it so that when the user clicks on a table row, they get one certain context menu and when they click on the background of the page, they get a different one.
I have already written the menus and gotten them working. The problem comes in when trying to figure out how to get the background's menu to show ONLY when clicking on the background and how to get the table row's menu to show when that is clicked.
I tried using document.body.oncontextmenu for the body and and setting the oncontextmenu function for each table row, but the body's oncontextmenu function overrides the row's so I get the wrong menu. The menu for the table rows DOES work if I stop using the body's menu, so that's not the issue.
I could be using the wrong events, so is there a different event for just the background (and not the elements on top of the background)? Or a way to "prioritize" the events so the table row's function takes precedence?
This is how the code looks:
var tableMenu;
var bodyMenu;
window.onload = function()
{
bodyMenu = new rightClickMenu("bodyMenu");
document.body.oncontextmenu = function() { bodyMenu.show(); tableMenu.hide(); }
bodyMenu.add("Add Entry", function()
{
alert("ADD");
});
tableMenu = new rightClickMenu("tableMenu", "tblSims");
simRows = getElementsByClassName("trSimRow");
for (var i in simRows)
simRows[i].oncontextmenu = function() { tableMenu.show(this.id.substring(2)); bodyMenu.hide(); }
tableMenu.add("Delete Entry", function(mac)
{
alert("DELETE");
});
document.body.onclick = function()
{
bodyMenu.hide();
tableMenu.hide();
};
}
You can capture the target element, e.g.:
$('*').click(function(e) {
alert(e.target);
alert(e.target.tagName);
if(e.target.tagName == 'html') {
// show background menu
}
});
You have to work with the Javascript Event Propagation model. What happens is that your click event is automatically passed down the layers of objects on a page that have been registered as event listeners, unless you explicitly tell it to stop, try something like this:
function setupClickHandlers()
{
document.getElementsByTagName('body')[0].onclick = doBodyMenu;
document.getElementById('tableID').onclick = doTableMenu;
}
function doBodyMenu()
{
//do whatever it does
}
function doTableMenu(e)
{
//do whatever it does
//stop the event propagating to the body element
var evt = e ? e : window.event;
if (evt.stopPropagation) {evt.stopPropagation();}
else {evt.cancelBubble=true;}
return false;
}
This should deal with the way each browser handles events.
$( document ).ready(function() {
var childClicked = false;
// myContainer is the nearest container div to the clickable elements
$("#myContainer").children().click(function(e) {
console.log('in element');
childClicked = true;
});
$("#myContainer").click(function(e){
if(!childClicked) {
console.log('in background');
e.preventDefault();
e.stopPropagation();
}
childClicked = false;
});
});
#myContainer {
width:200px;
height:200px;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myContainer" style="">
link
<div style="width:50px;height:50px;background-color: white;">
another link
</div>
</div>

Categories