Jquery - call function when blur+clicking certain element? - javascript

I want to call a function when a certain field gets blurred, but only if a certain element is clicked. I tried
$('form').click(function() {
$('.field').blur(function() {
//stuff
});
});
and
$('.field').blur(function() {
$('form').click(function() {
//stuff
});
});
But neither works, I reckon it's because the events happen simultaneously?

HTML
<form>
<input class="field" type="textarea" />
<input class="field" type="textarea" />
</form>
<div class="click-me-class" id="click-me">Click Me</div>
<div class="click-me-class">Click Me Class</div>
jQuery
$('.field').blur(function() {
$('#click-me').click(function(e) {
foo = $(this).data('events').click;
if(foo.length <= 1) {
// Place code here
console.log("Hello");
}
$(this).unbind(e);
});
});
You can test it out here: http://jsfiddle.net/WfPEW/7/

In most browsers, you can use document.activeElement to achieve this:
$('.field').blur(function(){
if ($(document.activeElement).closest('form').length) {
// an element in your form now has focus
}
});

I have edited my answer because we have to take into account that the event is asigned every time.
It is not 100% satisfactory, and I don't recommend this kind of complicated way of doing things, but it is the more approximate.
You have to use a global variable to take into account the fact that the field was blurred. In the window event, it is automatically reset to 0, but if the click on "click-me" is produced, it is verified before the window event, becase window event is bubbled later, it happens inmediately after the "click-me" click event
Working code
$(window).click(function(e)
{
$("#result").html($("#result").html()+" isBlurred=0<br/>");
isBlurred=0;
});
var isBlurred=0;
$('.field').blur(function() {
$("#result").html($("#result").html()+" isBlurred=1<br/>");
isBlurred=1;
});
$('#click-me').click(function(e) {
if(isBlurred==1)
{
$("#result").html($("#result").html()+" clicked<br/>");
}
});
".field" would be the input and "#click-me" would be the element clicked only just once.

Related

Prevent function execution twice within two triggered events

I have the following code:
myInput.change(function (e) { // this triggers first
triggerProcess();
});
myButton.click(function (e) { // this triggers second
triggerProcess();
});
The problem with the above is when I click myButton both events are triggered and triggerProcess() is fired twice which is not desired.
I only need triggerProcess() to fire once. How can I do that?
Small demo
You can have a static flag that disables any more triggers once the first trigger has occurred. Might look something like this:
var hasTriggered = false;
myInput.change(function (e) { // this triggers first
triggerProcess();
});
myButton.click(function (e) { // this triggers second
triggerProcess();
});
function triggerProcess () {
// If this process has already been triggered,
// don't execute the function
if (hasTriggered) return;
// Set the flag to signal that we've already triggered
hasTriggered = true;
// ...
}
For resetting the hasTriggered flag, that's entirely up to you and how this program works. Maybe after a certain event occurring in the program you'd want to reenable the ability to trigger this event again — all you'd need to do it set the hasTriggered flag back to true.
You can use the mousedown event, which will fire before the input is blurred, and then check if the input has focus by checking if it's the activeElement, and if it does have focus, don't fire the mousedown event, as the change event will fire instead.
Additionally, if you want a mousedown event to occur when the value hasn't changed, and the change event doesn't fire, you'll need a check for that as well
var myInput = $('#test1'),
myButton = $('#test2'),
i = 0;
myInput.change(function(e) { // this triggers first
$(this).data('prev', this.value);
triggerProcess();
});
myButton.mousedown(function(e) { // this triggers second
var inp = myInput.get(0);
if (document.activeElement !== inp || inp.value === myInput.data('prev'))
triggerProcess();
});
function triggerProcess() {
console.log('triggered : ' + (++i))
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="test1">
<br />
<br />
<button id="test2">
Click
</button>
In a fairly typical scenario where you have an input with a button next to ie, eg quick search.
You want to fire when the input changes (ie onblur) but also if the user clicks the button.
In the case where the user changes the input then clicks the button without changing input focus (ie no blur), the change event fires because the text has changed and the click event fires because the button has been clicked.
One option is to debounce the desired event handler.
You can use a plugin or a simple setTimeout/clearTimeout, eg:
$('#inp').change(debounceProcess)
$('#btn').click(debounceProcess);
function debounceProcess() {
if (debounceProcess.timeout != null)
clearTimeout(debounceProcess.timeout);
debounceProcess.timeout = setTimeout(triggerProcess, 100)
}
function triggerProcess() {
console.log('process')
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input id="inp">
<button id="btn">Click</button>
Use a real <button>BUTTON</button>. If you click on input text, alert is triggered, then once you leave the input text to click anywhere else, that unfocuses the input text which triggers the change event, so now 2 events have been triggered from the text input.
This is an assumption since the code provided is far from sufficient to give a complete and accurate answer. The HTML is needed as well as more jQuery/JavaScript. What is myInput and myButton actually referring to, etc.?
So I bet if you change...
var myButton = $('{whatever this is}'); and <input type='button'>
...TO:
var myButton = $("button"); and <button></button>
...you should no longer have an event trigger twice for an element.
This is assuming that triggerProcess() is a function that does something that doesn't manipulate the event chain or anything else involving events. This is an entirely different ballgame if instead of click() and change() methods you are using .trigger() or triggerHandler(), but it isn't. I'm not certain why such complex answers are derived from a question with very little info...?
BTW, if myInput is a search box and myButton is the button for myInput, as freedomn-m has mentioned, simply remove:
myButton.click(...
Leave myButton as a dummy. The change event is sufficient in that circumstance.
SNIPPET
var xInput = $('input');
var xButton = $('button'); //«———Add
xInput.on('change', alarm);
xInput.on('click', alarm);
xButton.on('click', alarm);
function alarm() {
return alert('Activated')
}
/* For demo it's not required */
[type='text'] {
width: 5ex;
}
b {
font-size: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id='f1' name='f1'>
<input type='text'>
<input type='button' value='BUTTON TYPE'>
<label><b>⇦</b>Remove this button</label>
<button>BUTTON TAG</button>
<label><b>⇦</b>Replace it with this button</label>
</form>

FocusEvent how to figure out it has been triggered through mouse or tab

I have some code which has some conditional branches if the FocusEvent has been triggered through a mouseclick outside of the input-box or if it has been tabbed out. It's pretty messy JS-Legacy code and I only have time to apply a hotfix here.
Doc for FocusEvent: https://developer.mozilla.org/en/docs/Web/API/FocusEvent
Unlike the Click event the FocusEvent does not have any informations about buttons pressed during the event triggering.
Does anybody has an idea how I can get this information? Via Google I only found workarounds - but I just can't believe that this FocusEvent has a way to receive the button pressed out of the box?
FocusEvent is clearly described as an experimental technology in the doc you linked. So what you ask may be added in the future. But for now it looks like you have no other choice but to use a workaround.
I made one to try:
var clickWhileFocused = false;
$("#testInput").on("tabbedOut", function () {
console.log("tabbedOut");
});
$("#testInput").on("clickedOut", function () {
console.log("clickedOut");
});
$(document).on("mousedown", function (e) {
if($("#testInput").is(":focus") && e.target.id != "testInput") {
$("#testInput").trigger("clickedOut");
clickWhileFocused = true;
}
});
$("#testInput").on("focusout", function () {
if(!clickWhileFocused) {
$("#testInput").trigger("tabbedOut");
}
clickWhileFocused = false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input type="text"/>
<input id="testInput" type="text" placeholder="#testInput"/>
<input type="text"/>

Changing an element's class on another element's click event

I have the following code, but it's not working. I'm trying to add or remove a class based on a click event.
Javascript:
function done(e){
if (e.hasClass("Gset")) {
e.removeClass("Gset")
}
else {
e.addClass("Gset")
}
}
HTML:
<h4 id="test">
<input type="checkbox" onClick="done(test)">
Ready?
</h4>
Here is a Jfiddle link showing it
Any help would be greatly appreciated!
Your code isn't working because e in your function is a DOM element, not a jQuery object. DOM elements don't have jQuery functions on them.
I should note that the only reason that it's a DOM element is that by giving the element an id, you've caused the browser to create an automatic global for it, which is why onClick="done(test)" works at all (the test there is a variable reference, and will pick up the automatic global).
The minimal fix is to make it a jQuery object:
function done(e){
e = $(e); // <===
if (e.hasClass("Gset")){e.removeClass("Gset") }
else {e.addClass("Gset") }
}
But a more thorough fix is to use toggleClass as well:
function done(e) {
$(e).toggleClass("Gset");
}
And even more thorough update would be to hook up the handler using jQuery rather than using the long-outdated onxyz attributes, not least because relying on automatic globals is error-prone (for instance, id="name" would fail):
$("#test input").on("click", function() {
$("#test").toggleClass("Gset");
});
.Gset {
background: yellow;
}
<h4 id="test">
<input type="checkbox">
Ready?
</h4>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Send the parameter as:
done(this)
And then in the function:
function done(e) {
e = $(e).parent();
if (e.hasClass("Gset")) {
e.removeClass("Gset")
} else {
e.addClass("Gset")
}
}
There's a simpler way:
function done(e) {
$(e).parent().toggleClass("Gset");
}
Reasons:
e, as passed as this will be a DOMElement, but hasClass, addClass and removeClass works only on jQuery objects.
It will be better if you avoid inline-event onClick.
HTML :
<h4 id="test">
<input type="checkbox">Ready?
</h4>
JS :
$('#test input').click(function(){
$('#test').toggleClass("Gset");
})
Hope this helps.

How do I detect when a div has lost focus?

Given the following markup, I want to detect when an editor has lost focus:
<div class="editor">
<input type="text"/>
<input type="text"/>
</div>
<div class="editor">
<input type="text"/>
<input type="text"/>
</div>
<button>GO</button>
EDIT: As the user tabs through the input elements and as each editor div loses focus (meaning they tabbed outside the div) add the loading class to the div that lost focus.
This bit of jquery is what I expected to work, but it does nothing:
$(".editor")
.blur(function(){
$(this).addClass("loading");
});
This seems to work, until you add the console log and realize it is triggering on every focusout of the inputs.
$('div.editor input').focus( function() {
$(this).parent()
.addClass("focused")
.focusout(function() {
console.log('focusout');
$(this).removeClass("focused")
.addClass("loading");
});
});
Here is a jsfiddle of my test case that I have been working on. I know I am missing something fundamental here. Can some one enlighten me?
EDIT: After some of the comments below, I have this almost working the way I want it. The problem now is detecting when focus changes to somewhere outside an editor div. Here is my current implementation:
function loadData() {
console.log('loading data for editor ' + $(this).attr('id'));
var $editor = $(this).removeClass('loaded')
.addClass('loading');
$.post('/echo/json/', {
delay: 2
})
.done(function () {
$editor.removeClass('loading')
.addClass('loaded');
});
}
$('div.editor input').on('focusin', function () {
console.log('focus changed');
$editor = $(this).closest('.editor');
console.log('current editor is ' + $editor.attr('id'));
if (!$editor.hasClass('focused')) {
console.log('switched editors');
$('.editor.focused')
.removeClass('focused')
.each(loadData);
$editor.addClass('focused');
}
})
A bit more complicated, and using classes for state. I have also added in the next bit of complexity which is to make an async call out when an editor loses focus. Here a my jsfiddle of my current work.
If you wish to treat entry and exit of the pairs of inputs as if they were combined into a single control, you need to see if the element gaining focus is in the same editor. You can do this be delaying the check by one cycle using a setTimeout of 0 (which waits until all current tasks have completed).
$('div.editor input').focusout(function () {
var $editor = $(this).closest('.editor');
// wait for the new element to be focused
setTimeout(function () {
// See if the new focused element is in the editor
if ($.contains($editor[0], document.activeElement)) {
$editor.addClass("focused").removeClass("loading");
}
else
{
$editor.removeClass("focused").addClass("loading");
}
}, 1);
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/8s8ayv52/18/
To complete the puzzle (get your initial green state) you will also need to also catch the focusin event and see if it is coming from the same editor or not (save the previous focused element in a global etc).
Side note: I recently had to write a jQuery plugin that did all this for groups of elements. It generates custom groupfocus and groupblur events to make the rest of the code easier to work with.
Update 1: http://jsfiddle.net/TrueBlueAussie/0y2dvxpf/4/
Based on your new example, you can catch the focusin repeatedly without damage, so tracking the previous focus is not necessary after all. Using my previous setTimeout example resolves the problem you have with clicking outside the divs.
$('div.editor input').focusin(function(){
var $editor = $(this).closest('.editor');
$editor.addClass("focused").removeClass("loading");
}).focusout(function () {
var $editor = $(this).closest('.editor');
// wait for the new element to be focused
setTimeout(function () {
// See if the new focused element is in the editor
if (!$.contains($editor[0], document.activeElement)) {
$editor.removeClass("focused").each(loadData);
}
}, 0);
});
Here's what worked for me:
$(".editor").on("focusout", function() {
var $this = $(this);
setTimeout(function() {
$this.toggleClass("loading", !($this.find(":focus").length));
}, 0);
});
Example:
http://jsfiddle.net/Meligy/Lxm6720k/
I think you can do this. this is an exemple I did. Check it out:
http://jsfiddle.net/igoralves1/j9soL21x/
$( "#divTest" ).focusout(function() {
alert("focusout");
});

Hiding divs based on checkboxes using jQuery

I have the following jQuery code:
<script> function hideMenteeQuestions() {
$("#menteeapp").hide();
$("textarea[name='short_term_goals']").rules("remove", "required");
$("textarea[name='long_term_goals']").rules("remove", "required");
}
function showMenteeQuestions() {
$("#menteeapp").show();
$("textarea[name='short_term_goals']").rules("add", {
required: true
});
$("textarea[name='long_term_goals']").rules("add", {
required: true
});
}
function hideMentorQuestions() {
$("#mentorapp").hide();
$("input[name='mentees']").rules("remove", "required");
}
function showMentorQuestions() {
$("#mentorapp").show();
$("input[name='mentees']").rules("add", {
required: true
});
}
</script>
<script>
$(document).ready(function(){
$("#mentee").change(function(){
if($(this).is(':checked')){
showMenteeQuestions();
}else{
hideMenteeQuestions();
}
});
$("#mentor").change(function(){
if($(this).is(':checked')){
showMentorQuestions();
}else{
hideMentorQuestions();
}
});
$('#mentee').change();
$('#mentor').change();
});
</script >
Also, here's the HTML for my checkboxes:
<input type="checkbox" name="type[]" id="mentor" value="mentor"><span class="checkbox">Mentor</span>
<input type="checkbox" name="type[]" id="mentee" value="mentee"><span class="checkbox">Mentee</span>
It's supposed to hide certain divs based on the checkbox you're selecting. It works when you click the checkboxes. However, I also want to trigger the change functions on page load. For some reason, it's only calling the first change function, which in this case is for #mentee. If I change the order, then the other one works. It never gets into the second change() call.
Any ideas?
Your description suggests that you have not properly wrapped your Javascript into a document.ready() function, i.e.
$(document).ready(function() {
// your code here
});
I expect what's happening is that one of your functions is throwing an exception because the DOM isn't yet properly ready.
Even if you have got a document.ready handler, I think the stuff about exceptions is still probably true - some condition is failing in both functions, but only on first load.
I would create a function that determines which box is checked and hides the div's accordingly. Then you can use that function as a callback for the change event as well as on load. for example:
$(document).ready(function(){
toggleDivs = function () {
if($("#mentor").is(':checked')){
hideMentorQuestions();
showMenteeQuestions();
}else if($("#mentee").is(':checked')){
hideMenteeQuestions();
showMentorQuestions();
}
}
$('#mentor, #mentee').change(toggleDivs);
toggleDivs();
});
It also seems to me like you want either-or in which case I would recommend using radio buttons rather than checkboxes
Got it to work by changing the hideMenteeQuestions() and showMenteeQuestions() functions:
function hideMenteeQuestions(){
$("#menteeapp").hide();
$("textarea[name='short_term_goals']").removeClass('required');
$("textarea[name='long_term_goals']").removeClass('required');
}
function showMenteeQuestions(){
$("#menteeapp").show();
$("textarea[name='short_term_goals']").removeClass('required').addClass('required');
$("textarea[name='long_term_goals']").removeClass('required').addClass('required');
}

Categories