I would like to implement such a directive for AngularJS: if used in input[text], on hitting enter the focus would move to next input. What would be the best way to accomplish this?
I have a very large form and would like to implement a way to go over the fields in a fast way.
Check this FIDDLE
There is ngKeypress directive in AngularJS, you can read more in here.
If your form is static as you mentioned, easiest way to accomplish what you need is passing next input's id (or index in my example). It's up to you how to provide ids, you can pass entire id or an index.
<input type="text" ng-model="field1" id="f_1"
ng-keypress="keypressHandler($event, 2)"/>
<br/>
<input type="text" ng-model="field2" id="f_2"
ng-keypress="keypressHandler($event, 3)"/>
<br/>
Then in your controller, if key is enter, get element by given id, then focus it.
$scope.keypressHandler = function(event, nextIdx){
if(event.keyCode == 13){
angular.element(
document.querySelector('#f_'+nextIdx))[0].focus();
}
}
As you can see, you can use ng-repeat like that by passing $index instead of hardcoded numbers and creating ids dynamically.
Another solution, use this directive:
angular.module('myApp').directive("nextFocus", nextFocus);
/** Usage:
<input next-focus id="field1">
<input next-focus id="field2">
<input id="field3">
Upon pressing ENTER key the directive will switch focus to
the next field id e.g field2
The last field should not have next-focus directive to avoid
focusing on non-existing element.
Works for Web, iOS (Go button) & Android (Next button) browsers,
**/
function nextFocus() {
var directive = {
restrict: 'A',
link: function(scope, elem, attrs) {
elem.bind('keydown', function(e) {
var partsId = attrs.id.match(/field(\d{1})/);
var currentId = parseInt(partsId[1]);
var code = e.keyCode || e.which;
if (code === 13) {
e.preventDefault();
document.querySelector('#field' + (currentId + 1)).focus();
}
});
}
};
return directive;
}
Related: angularjs move focus to next control on enter
Create a custom directive:
.directive('nextOnEnter', function () {
return {
restrict: 'A',
link: function ($scope, selem, attrs) {
selem.bind('keydown', function (e) {
var code = e.keyCode || e.which;
if (code === 13) {
e.preventDefault();
var pageElems = document.querySelectorAll('input, select, textarea'),
elem = e.srcElement
focusNext = false,
len = pageElems.length;
for (var i = 0; i < len; i++) {
var pe = pageElems[i];
if (focusNext) {
if (pe.style.display !== 'none') {
pe.focus();
break;
}
} else if (pe === e.srcElement) {
focusNext = true;
}
}
}
});
}
}
})
Source: https://stackoverflow.com/a/36960376/717267
Related
I have the following html:
<input type="checkbox" ng-model="user.show_value" ng-blur="update_show_value()">
A value (true / false) is fetched from the database and passed to the ng-model. Depending on it, the checkbox is checked / uncheked. The function inside ng-blur triggers the update in the database and works:
$scope.update_show_value() = function() {
if ($scope.user.show_value != undefined) {
$scope.loading = true;
//IF VALUE IS VALID, CALL THE UPDATEPIN FUNCTIONn
User.updatevalue($scope.user)
//IF SUCCESSFUL, GET VALUE
.success(function(data) {
$scope.loading = false;
$scope.formData = {}; //CLEAR FORM SO THAT USER CAN ENTER NEW DATA
$scope.user.show_value = {type : $scope.user[0].show_value}; //PASS VALUE IN OUR SCOPE
});
}
};
The issue is that I would have to use the checkbox from other devices that don't support the click event. From these devices I should use the equivalent of enter (keycode 13). So I added the onkeydown event to detect when the enter key is being pressed on the checkbox. Using an example from w3schools, I see it works (here is the example http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_event_key_keycode3)
<input type="checkbox" ng-model="user.show_value" onkeydown="keyCode(event)" ng-blur="update_show_value()">
Now I want to call the update function when the onkeydown event detects that the code 13 was pressed. Something like this:
<script>
function keyCode(event) {
var x = event.keyCode;
if (x == 13) {
alert ("You pressed the Escape key!");
update_show_value();
}
}
</script>
However, calling update_show_value inside the keycode function does not work. Actually, adding update_show_value inside the keycode function causes everything else not to work (ex. the alert)
So for some reason I think that scope functions cannot be called inside javascript functions. If that is true, is there a workaround?
You can get the scope object outside of Angular by using:
angular.element(event.target).scope(); // and then call whatever functions you want to on that scope object
You can define your own directive to handle keydown
angular.directive('myKeydown', [ function () {
return {
link: function(scope, elem, attrs) {
element.bind('keydown', function() {
scope.update_show_value();
});
}
};
}]);
Or just use ng-keydown instead: https://docs.angularjs.org/api/ng/directive/ngKeydown :
$scope.keyCode = function($event) {
. . .
$scope.update_show_value();
};
Remove () from $scope.update_show_value
Try Below Code:
<script>
var app=angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.update_show_value = function() {
alert(1);
};
});
</script>
I'm trying to buld a custom directive that is actually a wrapper around input field (to simplify formatting, encapsulate animations, etc.).
One goal is to use ngModel so my directive would also be compatible with ng-maxlength, ng-required and similar directives depending on ng-Model.
I've created this plunkr with my current state:
http://embed.plnkr.co/xV8IRqTmQmKEBhRhCfBQ/
My problem is that ng-required seems to be working, but invalidates only the complete form (so that form.$invalid becomes true), but not the element itself form.element.$invalid remains false.
Also, ng-maxlength / ng-minlength does not seem to have any effect at all.
What am I missing here? Any hints welcome :)
Hi everyone and thanks a lot for your answers!
I finally figured out what the missing piece for me was: the name attribute which is used by the form to reference the element MUST NOT be on the inner input field.
It has to reside on the outer element that carries the mg-model that also gets the other directives (that interact with the ng-model).
So, to illustrate this in more detail, before my template looked like:
<span class="custom-input-element">
<label for="{{elementId}}-input">{{elementLabel}}<span class="required-marker" ng-if="elementRequired">*</span></label>
<input id="{{elementId}}-input" type="text" name="{{elementName}}" ng-trim ng-model="value" ng-init="focused = false" ng-focus="focused = true" ng-blur="focused = false"/>
</span>
Which was used like
<custom-input id="foldername" name="foldername" label="Folder Name:"
ng-model="folder.name" ng-maxlength="15" ng-required="true"> </custom-input>
Notice the name={{elementName}} that basically overlayed the name="foldername" on my directive's tag.
After removing it from the directives template, the form references my directive and the ngModel on my directive for validation - the input and the inner ng-model keeps hidden. Thus, the interaction with other directives like ng-maxlength and mg-minlength and also custom directives/validators works as expected.
So now, not only the form gets invalidated but also each element is validated in the expected way.
I updated my plunker where everything is working as desired now: http://embed.plnkr.co/i3SzV8H7tnkUk2K9Pq6m/
Thanks for your time and your very valuable input!
I have created one that works, i'll try to show you the relevant part of the code.
The one really annoying point was to reattach the input and the validation to the form of the parent controller.
For this i had to cc a bunch of private code from angular :
/**
* start cc from angular.js to modify $setValidity of ngModel to retrieve the parent form...
*/
var VALID_CLASS = 'data-ng-valid',
INVALID_CLASS = 'data-ng-invalid',
PRISTINE_CLASS = 'data-ng-pristine',
DIRTY_CLASS = 'data-ng-dirty',
UNTOUCHED_CLASS = 'data-ng-untouched',
TOUCHED_CLASS = 'data-ng-touched',
PENDING_CLASS = 'data-ng-pending';
function addSetValidityMethod(context) {
var ctrl = context.ctrl,
$element = context.$element,
classCache = {},
set = context.set,
unset = context.unset,
parentForm = context.parentForm,
$animate = context.$animate;
classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
ctrl.$setValidity = setValidity;
function setValidity(validationErrorKey, state, controller) {
if (state === undefined) {
createAndSet('$pending', validationErrorKey, controller);
} else {
unsetAndCleanup('$pending', validationErrorKey, controller);
}
if (!isBoolean(state)) {
unset(ctrl.$error, validationErrorKey, controller);
unset(ctrl.$$success, validationErrorKey, controller);
} else {
if (state) {
unset(ctrl.$error, validationErrorKey, controller);
set(ctrl.$$success, validationErrorKey, controller);
} else {
set(ctrl.$error, validationErrorKey, controller);
unset(ctrl.$$success, validationErrorKey, controller);
}
}
if (ctrl.$pending) {
cachedToggleClass(PENDING_CLASS, true);
ctrl.$valid = ctrl.$invalid = undefined;
toggleValidationCss('', null);
} else {
cachedToggleClass(PENDING_CLASS, false);
ctrl.$valid = isObjectEmpty(ctrl.$error);
ctrl.$invalid = !ctrl.$valid;
toggleValidationCss('', ctrl.$valid);
}
// re-read the state as the set/unset methods could have
// combined state in ctrl.$error[validationError] (used for forms),
// where setting/unsetting only increments/decrements the value,
// and does not replace it.
var combinedState;
if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
combinedState = undefined;
} else if (ctrl.$error[validationErrorKey]) {
combinedState = false;
} else if (ctrl.$$success[validationErrorKey]) {
combinedState = true;
} else {
combinedState = null;
}
toggleValidationCss(validationErrorKey, combinedState);
parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
}
function createAndSet(name, value, controller) {
if (!ctrl[name]) {
ctrl[name] = {};
}
set(ctrl[name], value, controller);
}
function unsetAndCleanup(name, value, controller) {
if (ctrl[name]) {
unset(ctrl[name], value, controller);
}
if (isObjectEmpty(ctrl[name])) {
ctrl[name] = undefined;
}
}
function cachedToggleClass(className, switchValue) {
if (switchValue && !classCache[className]) {
$animate.addClass($element, className);
classCache[className] = true;
} else if (!switchValue && classCache[className]) {
$animate.removeClass($element, className);
classCache[className] = false;
}
}
function toggleValidationCss(validationErrorKey, isValid) {
validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
}
}
function arrayRemove(array, value) {
var index = array.indexOf(value);
if (index >= 0) {
array.splice(index, 1);
}
return index;
}
function isBoolean(value) {
return typeof value === 'boolean';
};
var SNAKE_CASE_REGEXP = /[A-Z]/g;
function snake_case(name, separator) {
separator = separator || '_';
return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
}
function isObjectEmpty(obj) {
if (obj) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
return false;
}
}
}
return true;
};
/**
* end of cc
*/
Then in the link function :
function(scope, element, attrs, ctrl, transclude){
[...]
scope.form = element.parent().controller('form');
var transcludedContent = transclude(scope.$parent);
// find the input
var fieldContent = findFormField(transcludedContent);
var ngModelCtrl = angular.element(fieldContent).controller('ngModel');
if(!ngModelCtrl){
throw 'transcluded form field must have a ng-model';
}
addSetValidityMethod({
ctrl: ngModelCtrl,
$element: angular.element(fieldContent),
set: function(object, property, controller) {
var list = object[property];
if (!list) {
object[property] = [controller];
} else {
var index = list.indexOf(controller);
if (index === -1) {
list.push(controller);
}
}
},
unset: function(object, property, controller) {
var list = object[property];
if (!list) {
return;
}
arrayRemove(list, controller);
if (list.length === 0) {
delete object[property];
}
},
parentForm: scope.form,
$animate: $animate
});
scope.form.$addControl(ngModelCtrl);
element.html(template);
$compile(element.contents())(scope);
element.find('.ng-form-field-content').append(transcludedContent);
// remove the control from the form, otherwise an ng-if that hide an invalid input will block your form
scope.$on(
"$destroy",
function handleDestroyEvent() {
scope.form.$removeControl(ngModelCtrl);
});
The template is a variable containing the html of my wrapping around the input. (it generates the label, put a start if required, show a check or cross sign if field valid/invalid,...).
EDIT :
With my directive i can do :
<div my-directive>
<input/textarea/select ng-model="", required/ng-required, ng-pattern, <custom directive validation>...
</div>
And it will give something like
<div my-directive>
<label for=<input'sname>>Texte</label>
<input [the input will all his attrs]/>
[some extra content]
</div>
I can even put some intermediary nodes or have multiple input that point to the same ng-model (like with checkbox/Radio buttons), however it won't works with different ng-models. I didn't push it that far.
I am using the typeahead directive in AngularJS and it works fine. However, I would like to have a button outside of the input that when clicked would show the typeahead dropdown. Here is a snippet of what I am after...
<li class="input">
<input focus-me="click" ng-model="something"
typeahead="state for state in Suggestions | filter:$viewValue:stateComparator" typeahead-focus typeahead-focus-first="false" typeahead-on-select="updateTagInput(newTagName)">
Open
</li>
Ok, I am having an absolutely terrible time trying to create a JSFiddle or even a Plunkr for this, so I will just give you the code for this directive.
This directive originally comes from..
This epic Bootstrap library!
..and I stole it and played with it. If you would like to use it, you will need the "Bootstrap" (its really a subset of angular directives) library that I linked to. You can make your own subset of this library, but I am not entirely sure of all of the dependencies my directive has as I am using the entire library in my project. Basically, you need any directive that starts with "typeahead".
As you can see, I have named the directive wwTypeahead (that "ww" is for WebWanderer!). It is a very easy to use directive and it works just like the original.
<input
class="form-control"
type="text"
spellcheck="false"
ng-model="selection"
ng-trim="false"
placeholder="Search Here"
ww-typeahead="key as key.label for key in list"
typeahead-on-select="selectionMade($item, $model, $label)"
typeahead-min-length="0"
/>
The really important part to note is the attribute typeahead-min-length="0" which has really been the heart of many discussions online. I managed to make that work.
This directive is meant to take the place of the typeahead directive in the library I linked to. Your typeahead list will be shown on focus of your input box. No, the list does not show on the click of a button, but hopefully getting there will be baby-steps from here. If you need help implementing that, I will be happy to help.
/*
NOTE:
The following directive is a modification of the
Angular typeahead directive. The normal directives,
unfortunately, do not allow matching on 0 length values
and the user may want a returned list of all values during
the lack of input.
This directives was taken from ...
http://angular-ui.github.io/bootstrap/
..and modified.
*/
angular.module('ui.directives', []).directive('wwTypeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',
function($compile, $parse, $q, $timeout, $document, $position, typeaheadParser)
{
var HOT_KEYS = [9, 13, 27, 38, 40];
return {
require:'ngModel',
link:function(originalScope, element, attrs, modelCtrl)
{
//SUPPORTED ATTRIBUTES (OPTIONS)
//minimal no of characters that needs to be entered before typeahead kicks-in
//var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
var testEval = originalScope.$eval(attrs.typeaheadMinLength);
var minSearch = !isNaN(parseFloat(testEval)) && isFinite(testEval) || 1;
//minimal wait time after last character typed before typehead kicks-in
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
//should it restrict model values to the ones selected from the popup only?
var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
//binding to a variable that indicates if matches are being retrieved asynchronously
var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
//a callback executed when a match is selected
var onSelectCallback = $parse(attrs.typeaheadOnSelect);
var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
//INTERNAL VARIABLES
//model setter executed upon match selection
var $setModelValue = $parse(attrs.ngModel).assign;
//expressions used by typeahead
var parserResult = typeaheadParser.parse(attrs.cmcTypeahead);
//pop-up element used to display matches
var popUpEl = angular.element('<typeahead-popup></typeahead-popup>');
popUpEl.attr({
matches: 'matches',
active: 'activeIdx',
select: 'select(activeIdx)',
query: 'query',
position: 'position'
});
//custom item template
if(angular.isDefined(attrs.typeaheadTemplateUrl))
{
popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
}
//create a child scope for the typeahead directive so we are not polluting original scope
//with typeahead-specific data (matches, query etc.)
var scope = originalScope.$new();
originalScope.$on('$destroy', function()
{
scope.$destroy();
});
var resetMatches = function()
{
scope.matches = [];
scope.activeIdx = -1;
};
var getMatchesAsync = function(inputValue)
{
var matchParsePrefix = originalScope.$eval(attrs.typeaheadParsePrefix);
var locals = {
$viewValue: inputValue.indexOf(matchParsePrefix) === 0 ? inputValue.substring(matchParsePrefix.length, (inputValue.length + 1)) : inputValue
};
isLoadingSetter(originalScope, true);
$q.when(parserResult.source(scope, locals)).then(function(matches)
{
//it might happen that several async queries were in progress if a user were typing fast
//but we are interested only in responses that correspond to the current view value
//if(matches && inputValue === modelCtrl.$viewValue)
/*
Ehh.. that didn't seem to work when I "cleared" the input box
*/
if(matches)
{
if(matches.length > 0)
{
scope.activeIdx = 0;
scope.matches.length = 0;
//transform labels
for(var i = 0; i < matches.length; i++)
{
locals[parserResult.itemName] = matches[i];
scope.matches.push({
label: parserResult.viewMapper(scope, locals),
model: matches[i]
});
}
scope.query = inputValue;
//position pop-up with matches - we need to re-calculate its position each time we are opening a window
//with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
//due to other elements being rendered
scope.position = $position.position(element);
scope.position.top = scope.position.top + element.prop('offsetHeight');
}
else if(minSearch === 0)
{
resetMatches();//temp
}
else
{
resetMatches();
}
isLoadingSetter(originalScope, false);
}
}, function()
{
resetMatches();
isLoadingSetter(originalScope, false);
});
};
resetMatches();
/*
Can't figure out how to make this work...*/
if(attrs.hasOwnProperty('typeaheadBindMatchReloader'))
{
$parse(attrs.typeaheadBindMatchReloader).assign(scope, function()
{
getMatchesAsync(element[0].value);
});
}
//we need to propagate user's query so we can higlight matches
scope.query = undefined;
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
var timeoutPromise;
//plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
modelCtrl.$parsers.unshift(function(inputValue)
{
resetMatches();
if((inputValue && inputValue.length >= minSearch)
|| minSearch === 0)
{
if(waitTime > 0)
{
if(timeoutPromise)
{
$timeout.cancel(timeoutPromise);//cancel previous timeout
}
timeoutPromise = $timeout(function()
{
getMatchesAsync(inputValue);
}, waitTime);
}
else
{
getMatchesAsync(inputValue);
}
}
if(isEditable)
{
return inputValue;
}
else
{
modelCtrl.$setValidity('editable', false);
return undefined;
}
});
modelCtrl.$formatters.push(function(modelValue)
{
var candidateViewValue, emptyViewValue;
var locals = {};
if(inputFormatter)
{
locals['$model'] = modelValue;
return inputFormatter(originalScope, locals);
}
else
{
//it might happen that we don't have enough info to properly render input value
//we need to check for this situation and simply return model value if we can't apply custom formatting
locals[parserResult.itemName] = modelValue;
candidateViewValue = parserResult.viewMapper(originalScope, locals);
locals[parserResult.itemName] = undefined;
emptyViewValue = parserResult.viewMapper(originalScope, locals);
return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;
}
});
scope.select = function(activeIdx)
{
//called from within the $digest() cycle
var locals = {};
var model, item;
locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
model = parserResult.modelMapper(originalScope, locals);
$setModelValue(originalScope, model);
modelCtrl.$setValidity('editable', true);
onSelectCallback(originalScope, {
$item: item,
$model: model,
$label: parserResult.viewMapper(originalScope, locals)
});
resetMatches();
//return focus to the input element if a mach was selected via a mouse click event
element[0].focus();
};
//bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
element.bind('keydown', function(evt)
{
//typeahead is open and an "interesting" key was pressed
if(scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1)
return;
evt.preventDefault();
if(evt.which === 40)
{
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
scope.$digest();
}
else if(evt.which === 38)
{
scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
scope.$digest();
}
else if(evt.which === 13 || evt.which === 9)
{
scope.$apply(function()
{
scope.select(scope.activeIdx);
});
}
else if(evt.which === 27)
{
evt.stopPropagation();
resetMatches();
scope.$digest();
}
});
// Keep reference to click handler to unbind it.
var dismissClickHandler = function(evt)
{
if(element[0] !== evt.target)
{
resetMatches();
scope.$digest();
}
else
{
getMatchesAsync(element[0].value);
}
};
$document.bind('click', dismissClickHandler);
originalScope.$on('$destroy', function()
{
$document.unbind('click', dismissClickHandler);
});
element.after($compile(popUpEl)(scope));
}
};
}]);
Call To Action:
Somebody PLEASE make a working example of this typeahead directive! I would forever be in debt to you! (well, not really but it would make me very happy)
DISCLAIMER:
I understand that this answer is in no way orthodox. I did not provide the askee (askee?) with a direct answer to the question, yet I did provide the tools that I believe are needed to get to his/her answer. I understand that I should spend the time to make a working example, but I am a very busy man and simply wished to share my work with the community, as I have seen this question asked too many times while I sit back and hold the answer. Please let me know if you have any issues, questions, or complications. I am happy to help.
Thanks!
<input
class="form-control"
spellcheck="false"
focus-me="click" ng-model="something"
ng-trim="false"
placeholder="Search Here"
uib-typeahead="key as key.label for key in list | filter:{label:$viewValue}"
typeahead-on-select="openTypeAhead($item, $model, $label)"
typeahead-min-length="0"
/>
in controller angularjs
$scope.openTypeAhead = ($item, $model, $label) =>{ console.log('arg =>',$item, $model, $label);}
I have created an application in angularjs with ckeditor plugin, I have created a directive for ckeditor, The application is working fine but the issue is that i need to set a max character length to be 50, so i put maxlength="50", but its not working,
Can anyone please tell me some solution for this
JSFiddle
html
<div data-ng-app="app" data-ng-controller="myCtrl">
<h3>CKEditor 4.2:</h3>
<div ng-repeat="editor in ckEditors">
<textarea data-ng-model="editor.value" maxlength="50" data-ck-editor></textarea>
<br />
</div>
<button ng-click="addEditor()">New Editor</button>
</div>
script
var app = angular.module('app', []);
app.directive('ckEditor', [function () {
return {
require: '?ngModel',
link: function ($scope, elm, attr, ngModel) {
var ck = CKEDITOR.replace(elm[0]);
ck.on('pasteState', function () {
$scope.$apply(function () {
ngModel.$setViewValue(ck.getData());
});
});
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
};
}
};
}])
function myCtrl($scope){
$scope.ckEditors = [{value: ''}];
}
You need to add an id to your textarea, like this:
<textarea data-ng-model="editor.value" maxlength="50" id="mytext" data-ck-editor></textarea>
You need to handle the key events for CKEDITOR:
window.onload = function() {
CKEDITOR.instances.mytext.on( 'key', function() {
var str = CKEDITOR.instances.mytext.getData();
if (str.length > 50) {
CKEDITOR.instances.mytext.setData(str.substring(0, 50));
}
} );
};
This works, however, note, that the content contains html tags as well, you might want to keep them,
I came across this same issue. I made this function which works with CKEditor to basically mimic the maxlength function.
window.onload = function() {
CKEDITOR.instances.mytext.on('key',function(event){
var deleteKey = 46;
var backspaceKey = 8;
var keyCode = event.data.keyCode;
if (keyCode === deleteKey || keyCode === backspaceKey)
return true;
else
{
var textLimit = 50;
var str = CKEDITOR.instances.mytext.getData();
if (str.length >= textLimit)
return false;
}
});
};
This function will check to make sure the input does not have more than the allowed characters.
If it does it will return false which simply does not allow any more inputs into the field.
If the user presses backspace or delete then it will return true which still allows the user to change their content if they reach the content limit.
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