Switching key events without a framework - javascript

I can't believe this hasn't been asked before (I searched and searched) but anyway.
How can I switch key events with pure JS?
For example, I want to switch the Enter button to mimic the Tab button. (I want to be able to push enter without submitting the form and instead switch fields to a Tab-like behavior e.i. switch form fields)

var enter = 13;
if (e.which === enter) {
// tabs to next field
getNextField(e.srcElement).focus();
// stops default enter
event.preventDefault();
event.stopPropagation();
}
implement getNextField() using .tabindex
function getNextField(obj) {
var next = obj.tabIndex + 1;
var all = document.getElementsByTagName("*");
for (var i in all) {
if (all[i].tabIndex === next) {
all[i].focus();
break;
}
}
}
This should give you a vague idea of how to do it. Flesh out the pseudocode yourself.

Related

Qualtrics Javascript keyboard presses

I've been given some javascript code for recording keyboard presses in Qualtrics. Seems to work fine when I preview the survey, but as soon as I use the distribute survey link, it does not work at all.
I'm using this code for before the event I want to record 'x' keyboard presses for:
Qualtrics.SurveyEngine.addOnload(function()
{
Event.observe(document, 'keydown', function (e) {
switch (e.keyCode) {
case 88: // 'x' was pressed
var totalpresses = Qualtrics.SurveyEngine.getEmbeddedData("xhits");
if (totalpresses == "NaN"){totalpresses=1;}
totalpresses = parseInt(totalpresses);
totalpresses = totalpresses + 1;
Qualtrics.SurveyEngine.setEmbeddedData("xhits",totalpresses);
}
});
});
and i'm using this code for following the event:
Qualtrics.SurveyEngine.addOnload(function()
{
var totalpresses = Qualtrics.SurveyEngine.getEmbeddedData("xhits");
Qualtrics.SurveyEngine.setEmbeddedData("finalxhits", totalpresses);
which gives me 'xhits' for the section I need it for, but only when I preview the survey.
I am using embedded data in the survey flow to create 'xhits' and 'finalxhits', yet finalxhits doesn't seem to show a value either (not that this is the problem).
I am very new to javascript so would appreciate any help.
I'm not sure what is causing your issue (I haven't tried to recreate it), but try this:
Qualtrics.SurveyEngine.addOnload(function()
{
var totalpresses = parseInt("${e://Field/xhits}");
if (isNaN(totalpresses)) totalpresses = 0;
Event.observe(document, 'keydown', function (e) {
switch (e.keyCode) {
case 88: // 'x' was pressed
totalpresses = totalpresses + 1;
Qualtrics.SurveyEngine.setEmbeddedData("xhits",totalpresses);
}
});
});
Is that second piece of code attached to a subsequent question? I'm not sure what purpose it serves, but you can just do that assignment in the survey flow:
finalxhits = ${e://Field/xhits}

How to validate textarea and checkboxes which are generated dynamically

This one seems really common question in StackOverflow. However, I am having difficulty in validating these textarea (Not to left blank) and checkboxes(At least one should be checked). I tried several validation Javascripts and frameworks but in vain.
I have textarea named "case_title0[]" whose will increase the number "0" to "1","2" and so on when user clicks "Add More" button. I want to validate at the point when user clicks the "Add More" button.
Secondly, I want the checkbox (name="editioncheck'+caseNum+'[]") which is dynamic as well to restrict user to leave it blank. The checkbox looks like "editioncheck0[]", "editioncheck1[]" and so on. It needs to be checked at least once to proceed to next "Add More" button. Until then "Add More" button should remain inactive.
So, I want these two type of validation in my form ie. textarea and checkbox.
Which is the simplest framework or custom code to use here?
I don't want fancy display as just alert() box should work in this regard.
Add common class to all textareas and common class to all checkboxes and perform validation.
<textarea class="t"></textarea>
<textarea class="t"></textarea>
<textarea class="t"></textarea>
<textarea class="t"></textarea>
<input type="checkbox" class="c">
function validate() {
var err = false;
$('.t').each(function(){
if($(this).text().length < 1) {
err = true;
return false;
}
});
if(!err) {
/* code to validate checkboxes like above */
}
return !err;
}
Finally, I have figured out the solution and I am going to post it here. As there is no exactly similar solution to my problem I had to code from scratch. While doing so lot of online resources helped me a lot.
To validate textarea on the fly (dynamic generate of text area when user clicks "Add More" button), here is solution I applied:
var csn='case_title'+caseno; //here "caseno" is incremental number to uniquely identify elements
var csum='case_summary'+caseno;
var caset=document.getElementById(csn).value;
var casesum=document.getElementById(csum).value;
if (caset == null || caset == "") {
alert("Executive Summary must be filled out");
caseNum --;
return false;
}
if (casesum == null || casesum == "") {
alert("Summaries with links must be filled out");
caseNum --;
return false;
Next, to validate the checkboxes I did as follows:
var edval='editioncheck'+caseno;
var checkBoxes=document.getElementsByClassName(edval);
var isChecked = false;
for (var i = 0; i < checkBoxes.length; i++) {
if ( checkBoxes[i].checked ) {
isChecked = true;
};
};
if ( isChecked ) {
//alert( 'At least one checkbox checked!' );
} else {
alert( 'Select at least one edition!' );
caseNum --;
return false;
}
The solution seems similar to the concept of fr34k, so thanks a lot. However, I found this online here http://jsfiddle.net/qyE97/
So, every time user click "Add More" button this script is executed and validates for the textarea and checkboxes.

Phonegap - Determine exact element active

I need to change the back button functionality of my phonegap project, which I've succeeded in doing without any problem. The only issue now, is that I need to further change the functionality based on if the user has a certain field selected.
Basically, if the user has clicked in a field with the id of "date-selector1", I need to completely disable the back button.
I was attempting to use document.activeElement, but it only returns the type of the element (input in this case), but I still want the functionality to work when they are in a general input, but not when they are in an input of a specific id.
EDIT
I tried all of the suggestions below, and have ended up with the following code, but still no success.
function pluginDeviceReady() {
document.addEventListener("backbutton", onBackKeyDown, false);
}
function onBackKeyDown() {
var sElement = document.activeElement;
var isBadElement = false;
var eList = ['procedure-date', 'immunization-date', 'lab-test-done', 'condition-onset', 'condition-resolution', 'medication-start-date', 'medication-stop-date', 'reaction-date'];
console.log("[[ACTIVE ELEMENT: --> " + document.activeElement + "]]");
for (var i = 0;i < eList.length - 1;i++) {
if (sElement == $(eList[i])[0]) {
isBadElement = true;
}
}
if (isBadElement) {
console.log('Back button not allowed here');
} else if ($.mobile.activePage.is('#main') || $.mobile.activePage.is('#family') || $.mobile.activePage.is('#login')) {
navigator.app.exitApp();
} else {
navigator.app.backHistory();
}
}
if you're listening for the back button you can add this if statement:
if (document.activeElement == $("#date-selector1")[0]) {
/*disable button here, return false etc...*/
}
or even better (Thanks to Jonathan Sampson)
if (document.activeElement.id === "date-selector1") {
/*disable button here, return false etc...*/
}
You can have a flag set when a user clicks on a field or you can have a click event (or any other type of event) when a user clicks on the field that should disable the back button.
From the documentation it looks like for the specific page that the backbuton is conditional on you can drop back-btn=true removing that back button.
http://jquerymobile.com/test/docs/toolbars/docs-headers.html
If you need complex conditional functionality you can just create your own button in the header or footer, style it using jquery-mobile widgets and implement your own click functionality.

JSF: How to submit the form when the enter key is pressed?

It would appear to be a simple requirement, but I haven't found a simple solution yet:
In a JSF 1.2 / Richfaces 3.3 webapp, I have a form with input components of various types, followed by an <a4j:commandButton> and a <h:commandButton>. The former resets the form, the second performs some action with the data entered.
My goal is to have this action triggered when the user presses the enter key while entering data. How can I do that?
Edit: Generally, I have more than one <h:commandButton> per <form>. I'd like to designate a particular one as default action. Also, I'd like the solution to play nice with AJAX (which we use extensively).
Unless you are using MSIE browser and in reality you've only one input field without a button, it should just be the default behaviour. Otherwise probably some (autogenerated) JS code has messed it up.
If you don't have textareas in the form, an easy fix would be the following:
<h:form onkeypress="if (event.keyCode == 13) submit();">
Or if you have textareas and you don't want to repeat the same keypress functions over all non-textarea input elements, run the following script during window onload.
for (var i = 0; i < document.forms.length; i++) {
var inputs = document.forms[i].getElementsByTagName('input');
for (var j = 0; j < inputs.length; j++) {
inputs[j].onkeypress = function(e) {
e = e || window.event;
if (event.keyCode == 13) {
this.form.submit();
return false;
}
};
}
}
Building on BalusC's answer I came up with the following (tested on IE and FireFox):
<h:form id="form" onkeypress="ifEnterClick(event, #{rich:element('searchButton')});">
where ifEnterClick is defined by:
/**
* Handler for onkeypress that clicks {#code targetElement} if the
* enter key is pressed.
*/
function ifEnterClick(event, targetElement) {
event = event || window.event;
if (event.keyCode == 13) {
// normalize event target, so it looks the same for all browsers
if (!event.target) {
event.target = event.srcElement;
}
// don't do anything if the element handles the enter key on its own
if (event.target.nodeName == 'A') {
return;
}
if (event.target.nodeName == 'INPUT') {
if (event.target.type == 'button' || event.target.type == 'submit') {
if (strEndsWith(event.target.id, 'focusKeeper')) {
// inside some Richfaces component such as rich:listShuttle
} else {
return;
}
}
}
if (event.target.nodeName =='TEXTAREA') {
return;
}
// swallow event
if (event.preventDefault) {
// Firefox
event.stopPropagation();
event.preventDefault();
} else {
// IE
event.cancelBubble = true;
event.returnValue = false;
}
targetElement.click();
}
}
Edit: Since selecting a value from Firefox form auto completion using the enter key fires a keydown event, but no keypress event, using onkeypress is preferable to onkeydown.
Just put this code in your JS file:
$('input,textarea').live('keydown',function(e) { // submit forms on pressing enter while focus is on any input elmnt inside form
if (e.keyCode == 13) {
$(this).closest('form').submit();
}
});
In retrospect, solving this problem with the means HTML provides is far less brittle and easier to maintain, as the answers to the following related question show:
Multiple submit buttons on HTML form – designate one button as default
Use the PrimeFaces component:
<!-- Default button when pressing enter -->
<p:defaultCommand target="submit"/>
Use this in combination with a focus component and you will rock!
<!-- Focus on first field, or first field with error -->
<p:focus context="feesboek"/>

Detecting Unsaved Changes

I have a requirement to implement an "Unsaved Changes" prompt in an ASP .Net application. If a user modifies controls on a web form, and attempts to navigate away before saving, a prompt should appear warning them that they have unsaved changes, and give them the option to cancel and stay on the current page. The prompt should not display if the user hasn't touched any of the controls.
Ideally I'd like to implement this in JavaScript, but before I go down the path of rolling my own code, are there any existing frameworks or recommended design patterns for achieving this? Ideally I'd like something that can easily be reused across multiple pages with minimal changes.
Using jQuery:
var _isDirty = false;
$("input[type='text']").change(function(){
_isDirty = true;
});
// replicate for other input types and selects
Combine with onunload/onbeforeunload methods as required.
From the comments, the following references all input fields, without duplicating code:
$(':input').change(function () {
Using $(":input") refers to all input, textarea, select, and button elements.
One piece of the puzzle:
/**
* Determines if a form is dirty by comparing the current value of each element
* with its default value.
*
* #param {Form} form the form to be checked.
* #return {Boolean} <code>true</code> if the form is dirty, <code>false</code>
* otherwise.
*/
function formIsDirty(form) {
for (var i = 0; i < form.elements.length; i++) {
var element = form.elements[i];
var type = element.type;
if (type == "checkbox" || type == "radio") {
if (element.checked != element.defaultChecked) {
return true;
}
}
else if (type == "hidden" || type == "password" ||
type == "text" || type == "textarea") {
if (element.value != element.defaultValue) {
return true;
}
}
else if (type == "select-one" || type == "select-multiple") {
for (var j = 0; j < element.options.length; j++) {
if (element.options[j].selected !=
element.options[j].defaultSelected) {
return true;
}
}
}
}
return false;
}
And another:
window.onbeforeunload = function(e) {
e = e || window.event;
if (formIsDirty(document.forms["someForm"])) {
// For IE and Firefox
if (e) {
e.returnValue = "You have unsaved changes.";
}
// For Safari
return "You have unsaved changes.";
}
};
Wrap it all up, and what do you get?
var confirmExitIfModified = (function() {
function formIsDirty(form) {
// ...as above
}
return function(form, message) {
window.onbeforeunload = function(e) {
e = e || window.event;
if (formIsDirty(document.forms[form])) {
// For IE and Firefox
if (e) {
e.returnValue = message;
}
// For Safari
return message;
}
};
};
})();
confirmExitIfModified("someForm", "You have unsaved changes.");
You'll probably also want to change the registration of the beforeunload event handler to use LIBRARY_OF_CHOICE's event registration.
In the .aspx page, you need a Javascript function to tell whether or not the form info is "dirty"
<script language="javascript">
var isDirty = false;
function setDirty() {
isDirty = true;
}
function checkSave() {
var sSave;
if (isDirty == true) {
sSave = window.confirm("You have some changes that have not been saved. Click OK to save now or CANCEL to continue without saving.");
if (sSave == true) {
document.getElementById('__EVENTTARGET').value = 'btnSubmit';
document.getElementById('__EVENTARGUMENT').value = 'Click';
window.document.formName.submit();
} else {
return true;
}
}
}
</script>
<body class="StandardBody" onunload="checkSave()">
and in the codebehind, add the triggers to the input fields as well as resets on the submission/cancel buttons....
btnSubmit.Attributes.Add("onclick", "isDirty = 0;");
btnCancel.Attributes.Add("onclick", "isDirty = 0;");
txtName.Attributes.Add("onchange", "setDirty();");
txtAddress.Attributes.Add("onchange", "setDirty();");
//etc..
The following uses the browser's onbeforeunload function and jquery to capture any onchange event. IT also looks for any submit or reset buttons to reset the flag indicating changes have occurred.
dataChanged = 0; // global variable flags unsaved changes
function bindForChange(){
$('input,checkbox,textarea,radio,select').bind('change',function(event) { dataChanged = 1})
$(':reset,:submit').bind('click',function(event) { dataChanged = 0 })
}
function askConfirm(){
if (dataChanged){
return "You have some unsaved changes. Press OK to continue without saving."
}
}
window.onbeforeunload = askConfirm;
window.onload = bindForChange;
Thanks for the replies everyone. I ended up implementing a solution using JQuery and the Protect-Data plug-in. This allows me to automatically apply monitoring to all controls on a page.
There are a few caveats however, especially when dealing with an ASP .Net application:
When a user chooses the cancel option, the doPostBack function will throw a JavaScript error. I had to manually put a try-catch around the .submit call within doPostBack to suppress it.
On some pages, a user could perform an action that performs a postback to the same page, but isn't a save. This results in any JavaScript logic resetting, so it thinks nothing has changed after the postback when something may have. I had to implement a hidden textbox that gets posted back with the page, and is used to hold a simple boolean value indicating whether the data is dirty. This gets persisted across postbacks.
You may want some postbacks on the page to not trigger the dialog, such as a Save button. In this case, you can use JQuery to add an OnClick function which sets window.onbeforeunload to null.
Hopefully this is helpful for anyone else who has to implement something similar.
General Solution Supporting multiple forms in a given page (Just copy and paste in your project)
$(document).ready(function() {
$('form :input').change(function() {
$(this).closest('form').addClass('form-dirty');
});
$(window).bind('beforeunload', function() {
if($('form:not(.ignore-changes).form-dirty').length > 0) {
return 'You have unsaved changes, are you sure you want to discard them?';
}
});
$('form').bind('submit',function() {
$(this).closest('form').removeClass('form-dirty');
return true;
});
});
Note: This solution is combined from others' solutions to create a general integrated solution.
Features:
Just copy and paste into your app.
Supports Multiple Forms.
You can style or make actions dirty forms, since they've the class "form-dirty".
You can exclude some forms by adding the class 'ignore-changes'.
The following solution works for prototype (tested in FF, IE 6 and Safari). It uses a generic form observer (which fires form:changed when any fields of the form have been modified), which you can use for other stuff as well.
/* use this function to announce changes from your own scripts/event handlers.
* Example: onClick="makeDirty($(this).up('form'));"
*/
function makeDirty(form) {
form.fire("form:changed");
}
function handleChange(form, event) {
makeDirty(form);
}
/* generic form observer, ensure that form:changed is being fired whenever
* a field is being changed in that particular for
*/
function setupFormChangeObserver(form) {
var handler = handleChange.curry(form);
form.getElements().each(function (element) {
element.observe("change", handler);
});
}
/* installs a form protector to a form marked with class 'protectForm' */
function setupProtectForm() {
var form = $$("form.protectForm").first();
/* abort if no form */
if (!form) return;
setupFormChangeObserver(form);
var dirty = false;
form.observe("form:changed", function(event) {
dirty = true;
});
/* submitting the form makes the form clean again */
form.observe("submit", function(event) {
dirty = false;
});
/* unfortunatly a propper event handler doesn't appear to work with IE and Safari */
window.onbeforeunload = function(event) {
if (dirty) {
return "There are unsaved changes, they will be lost if you leave now.";
}
};
}
document.observe("dom:loaded", setupProtectForm);
Here's a javascript / jquery solution that is simple. It accounts for "undos" by the user, it is encapsulated within a function for ease of application, and it doesn't misfire on submit. Just call the function and pass the ID of your form.
This function serializes the form once when the page is loaded, and again before the user leaves the page. If the two form states are different, the prompt is shown.
Try it out: http://jsfiddle.net/skibulk/Ydt7Y/
function formUnloadPrompt(formSelector) {
var formA = $(formSelector).serialize(), formB, formSubmit = false;
// Detect Form Submit
$(formSelector).submit( function(){
formSubmit = true;
});
// Handle Form Unload
window.onbeforeunload = function(){
if (formSubmit) return;
formB = $(formSelector).serialize();
if (formA != formB) return "Your changes have not been saved.";
};
}
$(function(){
formUnloadPrompt('form');
});
I recently contributed to an open source jQuery plugin called dirtyForms.
The plugin is designed to work with dynamically added HTML, supports multiple forms, can support virtually any dialog framework, falls back to the browser beforeunload dialog, has a pluggable helper framework to support getting dirty status from custom editors (a tinyMCE plugin is included), works within iFrames, and the dirty status can be set or reset at will.
https://github.com/snikch/jquery.dirtyforms
Detect form changes with using jQuery is very simple:
var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed
// check for form changes
if ($('#formId').serialize() != formInitVal) {
// show confirmation alert
}
I expanded on Slace's suggestion above, to include most editable elements and also excluding certain elements (with a CSS style called "srSearch" here) from causing the dirty flag to be set.
<script type="text/javascript">
var _isDirty = false;
$(document).ready(function () {
// Set exclude CSS class on radio-button list elements
$('table.srSearch input:radio').addClass("srSearch");
$("input[type='text'],input[type='radio'],select,textarea").not(".srSearch").change(function () {
_isDirty = true;
});
});
$(window).bind('beforeunload', function () {
if (_isDirty) {
return 'You have unsaved changes.';
}
});
var unsaved = false;
$(":input").change(function () {
unsaved = true;
});
function unloadPage() {
if (unsaved) {
alert("You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?");
}
}
window.onbeforeunload = unloadPage;
This is exactly what the Fleegix.js plugin fleegix.form.diff (http://js.fleegix.org/plugins/form/diff) was created for. Serialize the initial state of the form on load using fleegix.form.toObject (http://js.fleegix.org/ref#fleegix.form.toObject) and save it in a variable, then compare with the current state using fleegix.form.diff on unload. Easy as pie.
A lot of outdated answers so here's something a little more modern.
ES6
let dirty = false
document.querySelectorAll('form').forEach(e => e.onchange = () => dirty = true)
One method, using arrays to hold the variables so changes can be tracked.
Here's a very simple method to detect changes, but the rest isn't as elegant.
Another method which is fairly simple and small, from Farfetched Blog:
<body onLoad="lookForChanges()" onBeforeUnload="return warnOfUnsavedChanges()">
<form>
<select name=a multiple>
<option value=1>1
<option value=2>2
<option value=3>3
</select>
<input name=b value=123>
<input type=submit>
</form>
<script>
var changed = 0;
function recordChange() {
changed = 1;
}
function recordChangeIfChangeKey(myevent) {
if (myevent.which && !myevent.ctrlKey && !myevent.ctrlKey)
recordChange(myevent);
}
function ignoreChange() {
changed = 0;
}
function lookForChanges() {
var origfunc;
for (i = 0; i < document.forms.length; i++) {
for (j = 0; j < document.forms[i].elements.length; j++) {
var formField=document.forms[i].elements[j];
var formFieldType=formField.type.toLowerCase();
if (formFieldType == 'checkbox' || formFieldType == 'radio') {
addHandler(formField, 'click', recordChange);
} else if (formFieldType == 'text' || formFieldType == 'textarea') {
if (formField.attachEvent) {
addHandler(formField, 'keypress', recordChange);
} else {
addHandler(formField, 'keypress', recordChangeIfChangeKey);
}
} else if (formFieldType == 'select-multiple' || formFieldType == 'select-one') {
addHandler(formField, 'change', recordChange);
}
}
addHandler(document.forms[i], 'submit', ignoreChange);
}
}
function warnOfUnsavedChanges() {
if (changed) {
if ("event" in window) //ie
event.returnValue = 'You have unsaved changes on this page, which will be discarded if you leave now. Click "Cancel" in order to save them first.';
else //netscape
return false;
}
}
function addHandler(target, eventName, handler) {
if (target.attachEvent) {
target.attachEvent('on'+eventName, handler);
} else {
target.addEventListener(eventName, handler, false);
}
}
</script>
In IE document.ready will not work properly it will update the values of input.
so we need to bind load event inside the document.ready function that will handle for IE browser also.
below is the code you should put inside the document.ready function.
$(document).ready(function () {
$(window).bind("load", function () {
$("input, select").change(function () {});
});
});
I have found that this one works in Chrome with an exception... The messages being returned do not match those in the script:
dataChanged = 0; // global variable flags unsaved changes
function bindForChange() {
$("input,checkbox,textarea,radio,select").bind("change", function (_event) {
dataChanged = 1;
});
$(":reset,:submit").bind("click", function (_event) {
dataChanged = 0;
});
}
function askConfirm() {
if (dataChanged) {
var message =
"You have some unsaved changes. Press OK to continue without saving.";
return message;
}
}
window.onbeforeunload = askConfirm;
window.onload = bindForChange;
The messages returned seem to be triggered by the specific type of action I'm performing. A RELOAD displays a question "Reload Site?
And a windows close returns a "Leave Site?" message.

Categories

Resources