prepare to focus first active element in a container - javascript

I have a container on a page that should be prepared for focusing, i.e. when user pressed TAB button, the first active element in the container must be focused.
The simplest way I have thought so far is to find the last active element before the container, focus it and then blur it. Will it work? Is there a simpler way? How to find the last active element before the container (cross-browser)?
I do not want to change tabbed elements order, I just want to define the next element that will be selected.
Please use raw javascript, not frameworks.

The only thing I can think of is looping over the container child nodes and try focusing them until you succeed:
<script type="text/javascript">
document.onkeyup = function (e) {
if (!e)
e = window.event;
var keyCode = e.keyCode || e.which;
if (keyCode == 9) {
FocusFirst(document.getElementById("MyContainer"));
}
}
function FocusFirst(element) {
try {
element.focus();
}
catch(ex) {}
if (document.activeElement == element)
return true;
for (var i = 0; i < element.childNodes.length; i++) {
var oChild = element.childNodes[i];
if (FocusFirst(oChild))
return true;
}
return false;
}
</script>
Sample HTML tested with: (IE, Chrome)
<div id="MyContainer">
<div>hello</div>
<div><span><input type="text" /></span></div>
<div><input type="text" /></div>
</div>
This will focus the first input box of the container upon tab click.

Add a listener to the document and change focus onKeyUp. Remove the listener once tab has been pressed:
document.onkeyup = onKeyEvent;
function onKeyEvent (e) {
if (e.keyCode == 9) {
document.onkeyup = null;
document.getElementById ('whatever').focus();
}
}

Here is the solution that I've used finally.
I just create fake empty link in the beginnig of the container and focus it. The link will be removed when it lose focus.
function prefocusElement(node)
{
var fake = document.createElement("a");
fake.setAttribute("href", "#");
node.insertBefore(fake, node.firstChild);
fake.focus();
fake.addEventListener("blur", function(e) { node.removeChild(fake); }, false);
};

Related

Move cursor to next element on enter keeping track of tabindex

I am using following code to move to next element. Code is working fine except for the tabindex=-1. It does not skip elements with tabindex set to -1.
$('body').on('keydown', 'input, select', function(e) {
if (e.key === "Enter") {
var self = $(this), form = self.parents('form:eq(0)'), focusable, next;
focusable = form.find('input,a,select,button,textarea').filter(':visible');
next = focusable.eq(focusable.index(this)+1);
if (next.length) {
next.focus();
} else {
form.submit();
}
return false;
}
});
I have been searching for the solution but could not find one.
Can anyone please help.
You focus specificly the next element, you don't let the browser handle it. Therefor the tabindex=-1 is not working.
To use the tabindex you specified in the HTML you have to specify it in the Javascript aswell ( :not([tabindex="-1"]) ) :
focusable = form.find('input,a,select,button,textarea').filter(':not([tabindex="-1"]):visible');

How to remove all children of contenteditable element?

I need to delete all children of a div after clicking enter.
There is a div and event listener below.
<div id = "area" contenteditable="true"></div>
document.onreadystatechange = function(){
if(document.readyState == 'complete'){
document.getElementById("area").addEventListener("keypress" , public_mode);
}
function public_mode(){
var key = window.event.keyCode;
if (key == 13) {
sendMessage();
}
}
function sendMessage(){
var area = document.getElementById("area");
while (area.firstChild) {
area.removeChild(area.firstChild);
}
}
As you can see the contenteditable elements is added an element in according with clicking enter - it depends on browser what element will be added.In my case I use chrome and here are inserted div.
So, the result after clicking enter on the area but without removing
<div id = "area" contenteditable = "true">
Sckoriy
<div></div>
</div>
and , with removing
<div id = "area" contenteditable = "true">
<div></div>
<div></div>
</div>
But , the needed result is
<div id = "area" contenteditable = "true">
//Empty
</div>
The code mostly works, however there were two main issues.
keyCode is deprecated. you should be using key which turns the syntax of searching for a key into looking for a string. This means instead of 13 you just check to see if key is Enter.
Secondly you need to pass the event to your public_mode function so that you can read the key that has been pressed when the event occurs. You also need to use preventDefault to prevent it from adding a new line after removing everything from the original contentEditable area when it does detect Enter
document.onreadystatechange = function() {
if (document.readyState == 'complete') {
document.getElementById("area").addEventListener("keypress", public_mode);
}
function public_mode(event) {
var key = event.key;
if (key === "Enter") {
event.preventDefault();
sendMessage();
}
}
function sendMessage() {
var area = document.getElementById("area");
while (area.firstChild) area.removeChild(area.firstChild);
}
}
#area {
min-width: 100vw;
border: 1px solid black;
}
<div id="area" contenteditable="true"></div>
You could just set the innerHTML proprety to an empty string;
area.innerHTML = '';
target the dom by id
var s = document.getElementById("area");
save the number of childrens
var num = s.children.length;
and remove the num of childs of element
for(var i=0;i<num;i++){
s.children[0].remove()
}
and inner for some thext
s.innerHTML = "";
Pass the key event as an argument to your function.
Also, if you do not want the newline entered in your div, you can prevent the event from continuing with event.preventDefault().
document.onreadystatechange = function() {
if (document.readyState == 'complete') {
const area = document.getElementById('area')
area.addEventListener('keypress', public_mode);
area.focus();
}
}
function public_mode(event) {
if (window.event.keyCode == 13) {
sendMessage();
event.preventDefault();
}
}
function sendMessage() {
const area = document.getElementById('area');
while (area.firstChild) {
area.removeChild(area.firstChild);
}
}
<div id="area" contenteditable="true">Press Enter to erase me!</div>

How to move focus on next field when enter is pressed?

Can you please tell me how to move focus on to the next field when the enter key is press? I use the dform plugin (which converts JSON to a form).
I Googled it, but this not working. Why doesn't my focus move on to the next field?
JSFiddle: http://jsfiddle.net/5WkVW/1/
$(document).keypress(function(e) {
if(e.which == 13) {
// Do something here if the popup is open
alert("dd")
var index = $('.ui-dform-text').index(this) + 1;
$('.ui-dform-text').eq(index).focus();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<form id="testSuiteConfigurationform" name="testSuiteConfigurationform" method="post" class="ui-dform-form" novalidate="novalidate">
<label class="ui-dform-label">
<h3>Configuration Parameters</h3>
</label>
<div class="ui-dform-div inputDiv">
<fieldset class="ui-dform-fieldset">
<input type="text" id="totalRetryCount" name="totalRetryCount" tabindex="1" onblur="validateElement('Configuration', 'testSuiteConfigurationform','totalRetryCount')" class="ui-dform-text valid">
<legend class="ui-dform-legend">Total Retry Count</legend>
<label for="totalRetryCount" class="checked">✔</label>
</fieldset>
<fieldset class="ui-dform-fieldset">
<input type="text" id="totalRepeatCount" name="totalRepeatCount" tabindex="2" onblur="validateElement('Configuration', 'testSuiteConfigurationform','totalRepeatCount')" class="ui-dform-text">
<legend class="ui-dform-legend">Total Repeat Count</legend>
</fieldset>
<fieldset class="ui-dform-fieldset">
<select id="summaryReportRequired" name="summaryReportRequired" tabindex="3" onblur="validateElement('Configuration', 'testSuiteConfigurationform','summaryReportRequired')" class="ui-dform-select">
<option class="ui-dform-option" value="true">true</option>
<option class="ui-dform-option" value="false">false</option>
</select>
<legend class="ui-dform-legend">Summary Report Required</legend>
</fieldset>
<fieldset class="ui-dform-fieldset">
<select id="postConditionExecution" name="postConditionExecution" tabindex="4" onblur="validateElement('Configuration', 'testSuiteConfigurationform','postConditionExecution')" class="ui-dform-select">
<option class="ui-dform-option" value="ALWAYS">ALWAYS</option>
<option class="ui-dform-option" value="ON_SUCCESS">ON_SUCCESS</option>
</select>
<legend class="ui-dform-legend">Post Condition Execution</legend>
</fieldset>
</div>
</form>
*Note (from comments): It also needs to work on pages that do not have tabindex values set
It fails because this is the document in your code.
You want to use the index of the currently focused item (document.activeElement), or if you use delegated events you can make sure this is the current item.
This final version works whether there are tabindexes or not. It also wraps around:
JSFiddle 1: http://jsfiddle.net/TrueBlueAussie/5WkVW/11/
JSFiddle 2: http://jsfiddle.net/TrueBlueAussie/5WkVW/12/
They both use a custom jQuery selector that I add called :focusable to select all focusable element (including links):
// register jQuery extension
jQuery.extend(jQuery.expr[':'], {
focusable: function (el, index, selector) {
return $(el).is('a, button, :input, [tabindex]');
}
});
$(document).on('keypress', 'input,select', function (e) {
if (e.which == 13) {
e.preventDefault();
// Get all focusable elements on the page
var $canfocus = $(':focusable');
var index = $canfocus.index(this) + 1;
if (index >= $canfocus.length) index = 0;
$canfocus.eq(index).focus();
}
});
You can use the same custom selector in the event handler if you like. Then it will even work on anchor links (if you change the event to keydown instead of keypress):
e.g.
$(document).on('keydown', ':focusable', function (e) {
Example with link: http://jsfiddle.net/5WkVW/15/
This also uses a delegated on, listening for the keydown event on the document. It then applies the jQuery selector, it then applies the function to any matching element that caused the event. This is much more efficient as it only applies the selector at event time (rather than apply multiple event handler to each DOM matching element).
Old versions below:
JSFiddle: http://jsfiddle.net/TrueBlueAussie/5WkVW/3/
$(document).keypress(function(e) {
if(e.which == 13) {
// Do something here if the popup is open
//alert("dd")
var index = $('.ui-dform-text').index(document.activeElement) + 1;
$('.ui-dform-text').eq(index).focus();
}
});
*Note: alerts can interfere with focus, so use console.log for output like that and view in most browser's debug window (like Chrome's F12 debugging tools).
Update: http://jsfiddle.net/TrueBlueAussie/5WkVW/4/
This one wraps back to the first item from the last and also works on selects (the default behavior is blocked, so you can only use space to open or up/down to select options.
$('input,select').on('keypress', function (e) {
if (e.which == 13) {
e.preventDefault();
var $next = $('[tabIndex=' + (+this.tabIndex + 1) + ']');
console.log($next.length);
if (!$next.length) {
$next = $('[tabIndex=1]');
}
$next.focus();
}
});
Requested "document" version: http://jsfiddle.net/TrueBlueAussie/5WkVW/5/
$(document).on('keypress', 'input,select', function (e) {
if (e.which == 13) {
e.preventDefault();
var $next = $('[tabIndex=' + (+this.tabIndex + 1) + ']');
console.log($next.length);
if (!$next.length) {
$next = $('[tabIndex=1]');
}
$next.focus();
}
});
I've created a non-jQuery version. So only pure Javascript;
https://jsfiddle.net/mm0uctuv/2/
Javascript:
var inputs = document.querySelectorAll("input,select");
for (var i = 0 ; i < inputs.length; i++) {
inputs[i].addEventListener("keypress", function(e){
if (e.which == 13) {
e.preventDefault();
var nextInput = document.querySelectorAll('[tabIndex="' + (this.tabIndex + 1) + '"]');
if (nextInput.length === 0) {
nextInput = document.querySelectorAll('[tabIndex="1"]');
}
nextInput[0].focus();
}
})
}
HTML:
<form>
Field 1: <input type="text" tabindex="1"><br>
Field 3: <input type="text" tabindex="3"><br>
Field 2: <input type="text" tabindex="2">
</form>
On the top-level div, add onKeyDown={this.onKeyDown.bind(this)} and add the following method (ES6) to the same class as the div:
onKeyDown(event) {
if (event.keyCode === 13) {
event.preventDefault()
const inputs =
Array.prototype.slice.call(document.querySelectorAll("input"))
const index =
(inputs.indexOf(document.activeElement) + 1) % inputs.length
const input = inputs[index]
input.focus()
input.select()
}
}
The following code should do it; it uses the tabIndex property. Let us know if that's is not acceptable:
$(function() {
$('input').on('keypress', function(e) {
e.which !== 13 || $('[tabIndex=' + (+this.tabIndex + 1) + ']')[0].focus();
});
});
The drop down already has enter key slated for opening the drop down.
JS FIDDLE DEMO
To be able to do something before moving to the next form element, you can use the following version:
$(function() {
$(document).on('keypress', function(e) {
var that = document.activeElement;
if( e.which == 13 ) {
e.preventDefault();
alert( "dd" );
$('[tabIndex=' + (+that.tabIndex + 1) + ']')[0].focus();
}
});
});
DEMO
Try the following JavaScript code that I modified from your fiddle. The default behavior of the select elements will be to expand on the keypress. The plus sign at the beginning of +$(this).attr("tabindex")
Converts the text attribute value to int.
$(".ui-dform-text").keypress(function(e) {
if(e.which == 13) {
// Do something here if the popup is open
alert($(this).attr("tabindex"));
var index = +$(this).attr("tabindex") + 1;
$("[tabindex='" + index +"']").focus();
}
});
This is mostly a joke but here is a Vanilla JS version using the newest APIs as long as you have a modern browser, it should be bullet proof
Here's what's happening:
Select Elements, inputs, etc... (excluding disabled, hidden, etc...)
Using the spread syntax, convert array (NodeList) to an object (here it's NodeObject)
Loop through the Objects aka Elements aka Nodes
Each iteration will pass the current element (Node) and the next element (NextNode) to an arrow function.
Continue if NextNode is an element
Then add a keypress event to the current element
Inside the event:
Continue only if the enter key was pressed (using e.key NOT e.keyCode or e.which -- which are deprecated)
Stop the Form from being submitted
Focus the next element
If we can, Select the text in the next node
And just like that you have some really unreadable code that is mostly parenthesis and arrow functions :)
// NodeList of applicable inputs, select, button
let NodesArray = document.querySelectorAll(`
#form input:not([disabled])[type]:not([type=\"hidden\"]),
#form select:not([disabled]),
#form button:not([disabled])[type=\"submit\"]
`);
// Spread the array to an object so we can load the next node without
// keeping track of indexes (barf)
(NodesObject => {
// Node and NextNode are Elements.
// You can update and get data if you want
Object.keys(NodesObject).forEach(i => (({ Node, NextNode }) => {
// Break if we dont have a NextNode
if (NextNode === false) return;
Node.addEventListener('keypress', KeyboardEvent => {
// Only continue if event.key was "Enter"
if (KeyboardEvent.key !== "Enter") return;
// Dont submit, thx
KeyboardEvent.preventDefault();
// Do the thing
NextNode.focus();
// Not all elements have a select method
if (typeof NextNode.select === 'function') NextNode.select();
});
})({
Node: NodesObject[i],
NextNode: NodesObject[(parseInt(i) + 1)] ?? false
}));
})({ ...NodesArray });
it looks the same, but I offer something simple, maybe helpful, and easy to remember, and this is what I use
html
<input placeholder="nama">
<input placeholder="email">
<input placeholder="password">
<button>MASUK<button>
js
$('INPUT').keydown( e => e.which === 13?$(e.target).next().focus():"");
// This will work; add this code in your on ready function and define your parent element which includes child elements to be focused.
const mainDiv = document.getElementById(`auto-focuser`); //here your parent element you need to focus
const keyDownEvent = (event) => {
if (event.key === "Enter") {
if (event.target.tagName === "TEXTAREA" && (event.target.innerHTML !== "" && event.target.innerHTML.substr(-1) !== "\n"))
return;
if (event.target.attributes.tabindex) {
const nextElm = mainDiv.querySelectorAll(`[tabindex='${parseInt(event.target.attributes.tabindex.value) + 1}']`).item(0)
if (nextElm) {
nextElm.focus()
if (nextElm.tagName === "INPUT" || nextElm.tagName === "TEXTAREA") {
nextElm.select()
nextElm.selectionStart = nextElm.selectionEnd = nextElm.value.length;
}
event.preventDefault()
}
}
}
}
mainDiv?.addEventListener('keydown', keyDownEvent);
1.first = you should put 'textbox' on your class name in input
2.second = put specific id for each input
then write this code for select that element and go to next element.
I select each element by each id and put next() function on keypress of every input.
function next(event,elem){
if ( event.keyCode == 13)
{
var list = document.getElementsByClassName("textbox");
for (var i=0 ; i<list.length ; i++)
{
if (elem.id == list[i].id)
{
var index = i + 1;
list[index].focus();
}
}
}
}
event args use for keybord press//
elem args use for element that we press eneter

How to stay DRY - multiple inputs that trigger the same function on keypress?

This is the code that is working (but repetitive). I tried to replace the "getElementById" part with "getElementsByClassName", adding a class to the elements - but that didn't do it for me. Right now I have this anonymous function repeated once for each ID:
document.getElementById("gasCost").onkeypress = function(e) {
if(e.keyCode == 13) {
goButton();
}
}
HTML:
<input placeholder="How far are you going? (miles)" id="distance">
<input placeholder="How many MPG does your car get?" id="mileage">
<input placeholder="What's the average cost of gas?" id="gasCost">
Is it possible to define the function prior & call it to each ID? Is it possible to do it differently with classes? I'm open to all suggestions, but I'd prefer not to be using jQuery.
If you wanted to use classes you could do something like this:
function keyHandler(e) {
if(e.keyCode == 13) {
goButton();
}
}
var elements = document.getElementsByClassName("yourclasshere");
for (var i = 0; i < elements.length; i++)
elements[i].onkeypress = keyHandler;
(getElementsByClassName() returns a list, so you have to loop over each element in the list and bind a handler to them individually.)
Alternatively you could bind a single handler to a parent element that contains all of your inputs (perhaps to a form element?):
document.getElementById("someContainer").onkeypress = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
if (e.keyCode === 13 && e.target.tagName === "INPUT") {
goButton();
}
};
Demo: http://jsfiddle.net/9U9nn/
This works because the key events "bubble up" from the source element (your input) through all containers until they reach the document. (So, worst case, you could bind the handler directly to the document.) You can tell which element the event originated with by checking the event.target (or event.srcElement for older IE).
function submitOnEnter(e) {
if(e.keyCode == 13) {
goButton();
}
}
document.getElementById("gasCost").onkeypress = submitOnEnter;
document.getElementById("mileage").onkeypress = submitOnEnter;
Note that this is already the default behavior in HTML forms: pressing enter submits the form.

duplicate div when enter is hit while the input element in the div is in focus

I need help with duplicating div when enter is hit while the input element in the div is in focus
so there is a div which has an input element in it. I need to duplicate this div and place it right below the existing div whenever enter is hit while the cursor is in the input element.
Solutions which don't use jquery will be of great help.
Thanks
Here is a high level overview...
Listen for keypress event on the input element, for keyCode of 13.
Use cloneNode() to clone the node.
Use the most appropriate node insertion method, such as appendChild() to insert the cloned element.
try this
Demo
<div id='container'>
<div id='div1'>
<input type='text' onkeyup='clone_element(event)' />
</div>
</div>
<script>
function clone_element(evt) {
var charCode = (evt.which) ? evt.which : event.keyCode
if(charCode == 13){
var cDiv = document.getElementById("div1");
var clonedDiv = cDiv.cloneNode (true);
clonedDiv.id = "";
var container = document.getElementById ("container");
container.appendChild(clonedDiv);
}
}
</script>
You can use the below function to clone the div. This is likely to be the solution Alex mentioned. I have added the code.
function cloning(divid) {
var container = document.getElementById(divid);
var clone = document.getElementById('div_0').cloneNode(true);
clone.setAttribute('id','div_'+document.getElementById(divid).getElementsByTagName('div').length);
container.appendChild (clone);
}
call the above function when you hit enter.
<script language = "javascript">
function searchKeyPress(e)
{
// look for window.event in case event isn't passed in
if (typeof e == 'undefined' && window.event) { e = window.event; }
if (e.keyCode == 13)
{
alert("Enter received");
cloning('divid');
}
}
</script>
<input type="text" onkeydown="searchKeyPress()">

Categories