Detect when clicking on another input after a focusout with Firefox - javascript

Consider that I have a page with multiple lines of text.
When then user clicks on it, it turns into multiple text inputs so he can modify it.
When he clicks outside the text input, its is saved and the input is replaced by the text.
But if he clicks on another text input, I don't want to save it for now, so he doesn't have to click multiple times on a line to modify it.
I was able to that using the 'focusout' event. I detect that the user clicks ont another input using jQuery's e.relatedTarget.
It works great with Chrome but Firefox doesn't support well focusout. e.relatedTargetis always null
How can I achieve something similar with firefox ?
$('input').on('focusout', function(e) {
if (e.relatedTarget != null) {
$('#display').html("don't save for now")
} else {
$('#display').html("save the inputs");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<div>
<input type="text" />
</div>
<div>
<input type="text" />
</div>
</div>
<div id="display"></div>

You are correct that relatedTarget is not very useful in Firefox.
The workaround is to explicitly check what element recieves the focus. This is not available during the focusout event (as the new element has not received focus yet), but it will be directly thereafter. Using a timeout of 0 to move the request to the end of the current execution queue will reliable allow you to get the newly focused element, and check if it is one of the inputs or not.
$('input').on('focusout', function(e) {
setTimeout(function() {
if ($(document.activeElement).is('input')) {
$('#display').html("don't save for now")
} else {
$('#display').html("save the inputs");
}
}, 0);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<div>
<input type="text" />
</div>
<div>
<input type="text" />
</div>
</div>
<div id="display"></div>

Related

Element.focus() doesn't work when called within a function handler for addEventListener()

Here is my code. For some reason, this code does not give focus to the textbox with the id "dude" even though the paragraph tag with id "answer" does get the key code of the button that I clicked. It is like the line with the focus() command gets completely ignored.
document.getElementById("thing").addEventListener("keypress", function() {
myFunction(event);
});
function myFunction(event) {
document.getElementById("answer").innerHTML = event.keyCode;
document.getElementById("dude").focus();
}
<!DOCTYPE html>
<html>
<body>
<input type="text" id="thing">
<input type="text" id="dude">
<p id="answer"></p>
</body>
</html>
However, if I were to make a slight modification and put the line with the focus() outside of the addEventListener(), then the focus() would work. For example, the following code works:
document.getElementById("dude").focus();
document.getElementById("thing").addEventListener("keypress", function() {
myFunction(event);
});
function myFunction(event) {
document.getElementById("answer").innerHTML = event.keyCode;
}
<!DOCTYPE html>
<html>
<body>
<input type="text" id="thing">
<input type="text" id="dude">
<p id="answer"></p>
</body>
</html>
The above code would actually start by giving the "dude" textbox focus.
Is there any reason for this and anything that I can do about this?
The keypress event handler fires too early - the user hasn't finished pressing the key down and entering in the value at that point, so the focus reverts to the initial input field. See how if you change the focus after a setTimeout it'll work:
document.getElementById("thing").addEventListener("keypress", function() {
myFunction(event);
});
function myFunction(event) {
document.getElementById("answer").innerHTML = event.keyCode;
setTimeout(() => document.getElementById("dude").focus());
}
<input type="text" id="thing">
<input type="text" id="dude">
<p id="answer"></p>
Or watch for the keyup event instead:
document.getElementById("thing").addEventListener("keyup", function() {
myFunction(event);
});
function myFunction(event) {
document.getElementById("answer").innerHTML = event.keyCode;
document.getElementById("dude").focus();
}
<input type="text" id="thing">
<input type="text" id="dude">
<p id="answer"></p>
For example, the following code works:
Not exactly, because with that code, you're focusing the dude input on pageload, rather than when the thing input has stuff typed into it.
You also should avoid using keypress in modern code, it's deprecated:
This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.
Since this event has been deprecated, you should look to use beforeinput or keydown instead.
keyCode is too, technically, but the replacement for it - .code - isn't compatible everywhere.
Use the keyup event instead of keypress, because the default action of the keypress event sets the focus back to that input element.
document.getElementById("thing").addEventListener("keyup", function() {
myFunction(event);
});
function myFunction(event) {
document.getElementById("answer").innerHTML = event.keyCode;
document.getElementById("dude").focus();
}
<!DOCTYPE html>
<html>
<body>
<input type="text" id="thing">
<input type="text" id="dude">
<p id="answer"></p>
</body>
</html>
It seems like you are trying to shift focus from input "thing" to input "dude" after you complete a "keypress" or "keyup" on input "thing".
I don't understand the use case for this. But, IMO if you are trying to change the focus state after you input a value, I would recommend placing an event listener on the "change" event. You could simply press your "TAB" key on your keyboard after you are done inputting data into input "thing" and focus will be shifted to the input "dude" and the function will execute. Both achieved!
document.getElementById('thing').addEventListener('change', function (event) {
myFunction(event.target.value)
})
function myFunction(answer) {
document.getElementById('answer').innerText = answer
}
<input type="text" id="thing" />
<input type="text" id="dude" />
<p id="answer"></p>

Focus reverts to text input on first attempt to transfer it to another element; only when VoiceOver is active

Note: This question is specific to desktop/non-touch keyboard navigation on OS X. The question comes from a use case revealed in QA. The issue was not reported for Windows running NVDA or the like.
When testing with VoiceOver enabled, attempting to transfer focus from the input to another element (on the keyup event, filtered to the down arrow key), the targeted element gets focus for a moment before it immediately returns to the input element.
It only does so the first time, though. And only if the input is not empty. After that, everything seems to work as expected.
let
$results = $('#results');
$(document).on({
'keyup': event => {
event.preventDefault();
if (event.which == 40) {
$results.children().eq(0).focus();
}
}
}, '#main');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form action="" autocomplete="off">
<input type="text" id="main">
<div id="results">
<p tabindex="0">foo</p>
<p tabindex="0">bar</p>
<p tabindex="0">baz</p>
</div>
</form>

Detect new DOM elements and changes to input boxes - JQuery

Take a look at the following HTML:
<div id="tab1">
<input type="text">
</div>
<div id="tab2">
<input type="text">
<button>Add New Input Box</button> <!-- Adds another <input type="text"> in tab -->
</div>
<div id="tab3">
<input type="text">
<button>Hide Input Box</button> <!-- Adds style 'display:none' to input -->
</div>
I am looking for a way to detect changes to the DOM with JQuery. I am aware of MutationObserver objects and these are great but I would only like to detect changes in the following scenarios:
New HTML elements are added (see #tab2 button - 'Add new input box')
Text is entered into input box (see #tab1 input field)
And NOT detect changes where:
HTML element attributes are changed (see #tab3 button - 'Hide input box')
Of course MutationObserver will flag the changes when the HTML attributes change (which I don't want) but it will not flag when a user types into an input box (which I do want). For this reason I am thinking MutationObserver objects are not suitable for this problem (correct me if I'm wrong).
Are there any other things I can use for this?
My Scenario: Just to give you some more context, I am creating a save prompt to a data entry app that contains multiple tabs. It is easy to forget to save your changes before moving to the next tab but sometimes you may want to move to another tab without saving yet. I am trying to show the user an alert informing them that they have not saved when they click the next tab. Some fields can be hidden/shown on the click of a button which MutationObserver will detect as a change to the DOM even though they may not have entered any new values. Some values can be pulled from another location at the click of a button via ajax which although the user has not typed anything, it should still be flagged as a change that needs to be saved as the DOM has changed.
To detect new elements added or removed from tab2, only add and remove elements, you may use the events: DOMNodeInserted DOMNodeRemoved, while for the changes to the input field you may use the input event.
These events are deprecated because they will be removed from the web.
For a better description see Mutation events
$(function () {
$('div, #tab1 input').on('DOMNodeInserted DOMNodeRemoved webkitTransitionEnd input', function (event) {
switch (event.type) {
case 'input':
if (event.target.parentNode.id == 'tab1') {
event.stopImmediatePropagation();
alert('Content changed');
}
break;
case 'DOMNodeInserted':
if (event.target.parentNode.id == 'tab2') {
alert('Content added!');
}
break;
case 'DOMNodeRemoved':
if (event.target.parentNode.id == 'tab2') {
alert('Content removed');
}
break;
}
});
$('#add').on('click', function (e) {
$(this).after('<input type="text">');
});
$('#remove').on('click', function (e) {
$(this).siblings('input').remove();
});
$('#tab3 button').on('click', function (e) {
$(this).siblings('input').toggle();
})
});
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<div id="tab1">
<input type="text">
</div>
<div id="tab2">
<input type="text">
<button id="add">Add New Input Box</button>
<button id="remove">Remove All Input Boxes</button>
</div>
<div id="tab3">
<input type="text">
<button>Hide/Show Input Box</button>
</div>

Text box focusout event and enter issues in IE9

I have a text input and one button of type submit adjacent to the input. I have bound focusout event-handler to the input and click event-handler to the button. Now when I focus on the input and then press enter, the focusout event-handler gets triggered and the buttons-event handler gets triggered. I want to trigger focusout only when text box focus is lost. What should I do ?
Code :-
<div >
<span>Local Currency: </span>
<input type='text' id='txtFocusElement' />
<button id="btnClickElement" >
<span> Add new line</span>
</button>
</div>
I used selector as:
$("#txtFocusElement").bind("focusout", function() {
console.log('focusout');
})
$("#btnClickElement").bind("click", function() {
console.log('click');
})
and written above code in one function which I call at the time of loading document.
Its IE9 issue with the HTML button tag.
So we should try it with HTML input tag with attribute type as button.
above code can be rewritten as :
<div >
<span>Local Currency: </span>
<input type='text' id='txtFocusElement' />
<input id="btnClickElement" type="button" value="Add new line" />
</div>
Using this my problem gets solved .
You could try to change the selector to input[type=text].
This will only get triggered when you focus out on a text field.
Try to attach to a mouseup event instead of click, that is the only solution I see for now
You can also add a check for mouse button which was pressed, so it wouldn't fire if user would press a right button
var ClickElementEventHandler = function(e) {
if (e.which != 1) return;
//your code
};
$("#btnClickElement").bind("mouseup",ClickElementEventHandler)

Why isn't this textarea focusing with .focus()?

I have this code to focus a textarea when the user clicks on the "Reply" button:
$('#reply_msg').live('mousedown', function() {
$(this).hide();
$('#reply_holder').show();
$('#reply_message').focus();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
<div id="reply_msg">
<div class="replybox">
<span>Click here to <span class="link">Reply</span></span>
</div>
</div>
<div id="reply_holder" style="display: none;">
<div id="reply_tab"><img src="images/blank.gif" /> Reply</div>
<label class="label" for="reply_subject" style="padding-top: 7px; width: 64px; color: #999; font-weight: bold; font-size: 13px;">Subject</label>
<input type="text" id="reply_subject" class="input" style="width: 799px;" value="Re: <?php echo $info['subject']; ?>" />
<br /><br />
<textarea name="reply" id="reply_message" class="input" spellcheck="false"></textarea>
<br />
<div id="reply_buttons">
<button type="button" class="button" id="send_reply">Send</button>
<button type="button" class="button" id="cancel_reply_msg">Cancel</button>
<!--<button type="button" class="button" id="save_draft_reply">Save Draft</button>-->
</div>
</div>
It shows the reply form, but the textarea won't focus. I'm adding the textarea via AJAX which is why I am using .live(). The box that I add shows (I even add #reply_msg via AJAX and stuff happens when I mouse down on it) but it won't focus on the textarea.
A mouse-click on a focusable element raises events in the following order:
mousedown
focus
mouseup
click
So, here's what's happening:
mousedown is raised by <a>
your event handler attempts to focus the <textarea>
the default event behavior of mousedown tries to focus <a> (which takes focus from the <textarea>)
Here's a demo illustrating this behavior:
$("a,textarea").on("mousedown mouseup click focus blur", function(e) {
console.log("%s: %s", this.tagName, e.type);
})
$("a").mousedown(function(e) {
$("textarea").focus();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
reply
<textarea></textarea>
So, how do we get around this?
Use event.preventDefault() to suppress mousedown's default behavior:
$(document).on("mousedown", "#reply_msg", function(e) {
e.preventDefault();
$(this).hide();
$("#reply_message").show().focus();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
reply
<textarea id="reply_message"></textarea>
Focusing on something from an event handler that, itself, grants focus, is always problematic. The general solution is to set focus after a timeout:
setTimeout(function() {
$('#reply_message').focus();
}, 0);
That lets the browser do its thing, and then you come back and yank focus over to where you want it.
Could it be the same issue as this? jQuery Textarea focus
Try calling .focus() after .show() has completed.
$('#reply_msg').live('mousedown', function() {
$(this).hide();
$('#reply_holder').show("fast", function(){
$('#reply_message').focus();
});
});
I ran into this issue today and in my case, it was caused by a bug in jQuery UI (v1.11.4) which causes textarea elements inside of draggable/droppable elements to stop default click behavior before the textarea receives the focus click.
The solution was to rework the UI so that the textarea no longer appears inside the draggable element.
This was a particularly difficult issue to debug, so I am leaving an answer here in case others find it helpful.
It might also come from the browser. I'm doing a project in Vue, and el.focus() on textarea works in Chrome (v101), but not in Firefox (v100.0).

Categories

Resources