Change an element's onfocus handler with Javascript? - javascript

I have a form that has default values describing what should go into the field (replacing a label). When the user focuses a field this function is called:
function clear_input(element)
{
element.value = "";
element.onfocus = null;
}
The onfocus is set to null so that if the user puts something in the field and decides to change it, their input is not erased (so it is only erased once). Now, if the user moves on to the next field without entering any data, then the default value is restored with this function (called onblur):
function restore_default(element)
{
if(element.value == '')
{
element.value = element.name.substring(0, 1).toUpperCase()
+ element.name.substring(1, element.name.length);
}
}
It just so happened that the default values were the names of the elements so instead of adding an ID, I just manipulated the name property. The problem is that if they do skip over the element then the onfocus event is nullified with clear_input but then never restored.
I added
element.onfocus = "javascript:clear_input(this);";
In restore_default function but that doesn't work. How do I do this?

Use
element.onfocus = clear_input;
or (with parameters)
element.onfocus = function () {
clear_input( param, param2 );
};
with
function clear_input () {
this.value = "";
this.onfocus = null;
}
The "javascript:" bit is unnecessary.

It looks like you don't allow the fields to be empty, but what if the user puts a single or more spaces in the field? If you want to prevent this, you need to trim it. (See Steven Levithans blog for different ways to trim).
function trim(str) {
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
If you really want to capitalize the strings you could use:
function capitalize(str) {
return str.substr(0, 1).toUpperCase() + str.substr(1).toLowerCase();
}
By clearing the onfocus event you have created a problem that should not have been there. An easier solution is to just add an if-statement to the onfocus event, so it only clears if it is your default value (but I prefer to select it like tvanfosson suggested).
I assume that you on your input-elements have set the value-property so that a value is shown in the input-elements when the page is displayed even if javascript is disabled. That value is available as element.defaultValue. Bonuses by using this approach:
You only define the default value in one place.
You no longer need to capitalize any value in your handlers.
The default value can have any case (like "John Y McMain")
The default value no longer needs to be the same as the name of the element.
.
function clear_default(element) {
if (trim(element.value) == element.defaultValue ) { element.value = ""; }
}
function restore_default(element) {
if (!trim(element.value).length) { element.value = element.defaultValue;}
}

I would suggest that you handle it a little differently. Instead of clearing the value, why not just highlight it all so that the user can just start typing to overwrite it. Then you don't need to restore the default value (although you could still do so and in the same way if the value is empty). You also can leave the handler in place since the text is not cleared, just highlighted. Use validation to make sure the value is not the original value of the input.
function hightlight_input(element) {
element.select();
}
function restore_default(element) // optional, do we restore if the user deletes?
{
if(element.value == '')
{
element.value = element.name.substring(0, 1).toUpperCase()
+ element.name.substring(1, element.name.length);
}
}

<!-- JavaScript
function checkClear(A,B){if(arguments[2]){A=arguments[1];B=arguments[2]} if(A.value==B){A.value=""} else if(A.value==""){A.value="Search"}}
//-->
<form method="post" action="search.php">
<input type="submit" name="1">
<input type="text" name="srh" Value="Search" onfocus="checkClear(this,'Search')" onblur="checkClear(this,' ')">
</form>

Related

knockout.js: can I catch an event before value update that allows me to stop the update?

I know I can catch a keydown event on an input box and return false from the handler to stop the key press from being applied to an input box. I'd like to do something on a higher level with knockout bindings, i.e., I want to stop an update that is about to happen, say, after checking that the value typed in the input box would be illegal. I don't want the illegal value be applied to my view-model and then I would have to undo it "manually". I want to stop it before the view model is changed in any way.
Another real use-case I am having is, I want to change something about the view-model before the input is being applied to it. For example, I might want to save a copy of the present state before updating it.
How can I do this within the knockout framework? I could possibly handle mounsedown or focus events to prepare to save the value and then let the update happen and then undo it if needed, but I want to stop the update itself.
Here is what I tried:
<html>
<head>
<title>Initial Write Protect Example</title>
<script src="knockout-3.5.0.debug.js" type="text/javascript"></script>
<script type="text/javascript">
const data = {
value: ko.computed({
owner: this,
read: function() { return "foo"; },
write: function(x) { console.log("writing: " + x); debugger; }
}),
};
let allowKey = true;
const changeFn = function(x) { console.log("changed: ", x, data.value()); debugger; return false; };
const beforeChangeFn = function(x) { console.log("about to change: ", x, data.value()); debugger; return false; };
const keydownFn = function(x,event) { console.log("keydown: " + event.key + " allow? " + allowKey); return allowKey; };
const keyupFn = function(x,event) { console.log("keyupn: " + event.key); return false; };
</script>
</head>
<body>
<p>
<span>Current Value:</span>
<span data-bind="text: value"></span>
<br/>
<input data-bind="value: value,
event:{
change: changeFn,
beforeChange: beforeChangeFn,
keydown: keydownFn,
keyup: keyupFn
}"></input>
</p>
<script type="text/javascript">ko.applyBindings(data);</script>
</body>
</html>
From this it is clear that while I can stop a key to take effect (setting allowKey to false in the debugger), I cannot stop the change, no beforeChange event is sent (it's mentioned once in the ko source code, so I figured I'd try it), and the change event comes after the ko.observable write. So, it's too late, the view model was already changed when the change event fires.
I guess I can go into the knockout source code to fix myself a nice beforeChange event whose return value of false would stop the further processing (and actually revert the value in the input box to what it was before.) But I wonder isn't there already some way I should go instead?
UPDATE:
I have now learned that I can subscribe to the beforeChange event for an observable:
observable.subscribe((newValue, eventName) => handle(newValue, eventName, 'beforeChange');
I made the two arguments here explicit to point out that the new value is not available in these handlers. This is a shame, because I need the new value too.
Here's how you can use a writable computed to prevent updates from propagating to your source observable.
You can use an extender to make it easy to reuse. I wrote the extender to accept a predicate function that takes a single string and returns either true or false. Depending on what you want to do with it, you might want to support a predicate that takes prevValue and nextValue. That would result in validator(target(), str).
Note: it can be a bad user experience for people to have an unresponsive input field, so make sure to provide feedback to the user typing in the input field.
ko.extenders.validate = function(target, validator) {
return ko.computed({
read: target,
write: str => {
if (!validator(str)) {
console.log(`Attempt to update ${data()} to ${str}, which is invalid.`);
target.notifySubscribers(true);
} else data(str);
}
}).extend({ notify: "always" });
}
const data = ko.observable("bdfgh");
ko.applyBindings({
data: data.extend({ validate: charsAreInAlphabeticalOrder })
});
function charsAreInAlphabeticalOrder(str) {
if (str.length <= 1) return true;
return (
str[0].localeCompare(str[1]) === -1 &&
charsAreInAlphabeticalOrder(str.slice(1))
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<label>
Enter characters alpabetically
<input type="text" data-bind="textInput: data">
</label>

How to Change Specific Text in an Inputbox Directly on User Input?

I have an input field in my code: <input type=text name=code id=code>.
What I want to do is to convert a specific text to another one as the user types in the field.
Let me explain more. When the user enters 31546 in the input, I want that text to directly convert to HELLO.
I know this can be done using JavaScript/jQuery, but I can't have any ideas on how to achieve this. How can I?
P.S. If it's easier to work with a textarea, I'm ready to change my input to a textarea.
EDIT: I got a code from this StackOverflow post which detects any changes to an element,
$('.myElements').each(function() {
var elem = $(this);
// Save current value of element
elem.data('oldVal', elem.val());
// Look for changes in the value
elem.bind("propertychange change click keyup input paste", function(event){
// If value has changed...
if (elem.data('oldVal') != elem.val()) {
// Updated stored value
elem.data('oldVal', elem.val());
// Do action
....
}
});
});
but I am not sure how to utilise this for what I want.
Please bear with me as I am yet a fledgling in this domain.
Thank you.
One option is to set up a keyup event for your input and then replace the value as the user types. For example:
$('input').keyup(function () {
var val = $(this).val();
var newVal = val.split('31546').join('HELLO');
if (newVal !== val) {
$(this).val(newVal);
}
});
You can use keyup event as,
$(document).on('keyup', '#code', function() {
$('#code').val(convertedValue($('#takeInput').val()));
})
function convertedValue(val) {
return 'hello';
}
This may help you :--
<input type="text" id="code">
$('#code').bind('change click keyup onpaste', function(ele){
var origVal = ele.target.value;
if(origVal.indexOf("123") !== -1){
ele.target.value = origVal.replace("123","Hello");
}
});

How to not clear textarea value when searching with Javascript onsearch Event

I'm using this method http://www.w3schools.com/jsref/event_onsearch.asp
What I want is the text that I searched with to stay in the textarea or at least get back to the textarea, not disappear because I click the enter button.
I understand that it clears the text because in the link they describe the function like this: "The onsearch event occurs when a user presses the "ENTER" key or clicks the "x" button in an element with type="search".
So it acts as if I click the x button, although, there must be a way to get the text back there after?
This is my current code html code
<form> <input type="search" name="search" id="searchid" onsearch="OnSearch(this)"/> </form>
This is my javasript/jquery
function OnSearch(input) {
alert("The current value of the search field is " + input.value);
$("#searchid").val(input.value);
}
What happens now is that it correctly alerts the value the textarea is holding, although it wont add back the textarea value.
EDIT: It seems like the page reloads, how can i insert code that runs after page reload?
Well I have an alternative. Since you cannot avoid the clear functionality you can store the text each time keypressed in a global variable and if x is pressed retain the value in textbox. Below is the code:
DEMO HERE
var text="";
function OnSearch(input) {
if(input.value == "") {
$("#searchid").val(text);
}
else {
alert("You searched for " + input.value);
}
}
$(document).on('keyup','#searchid', function (e) {
text=$(this).val();
console.log(text);
});
UPDATE
if your html is inside the form you can do as below:
Check in document.ready if it already had a text and if yes set it!!
$(document).ready(function()
{
if(localStorage.getItem("text")!="")
{
$("#searchid").val(localStorage.getItem("text"));
}
});
function OnSearch(input) {
if(input.value == "") {
$("#searchid").val(localStorage.getItem("text"));
}
else {
alert("You searched for " + input.value);
}
}
$(document).on('keyup','#searchid', function (e) {
localStorage.setItem("text",$(this).val());
});
I think this will help you to display the alert dialog symbol as that:
HTML:
<input type="search" name="search" id="searchid"/>
Javascript:
document.getElementById("searchid").onsearch = function() {yourfunctionname()};
/* Put this before the below function or in the top of the document */
function yourfunctionname(){
var x = document.getElementById("searchid").value;
alert("The current value of the search field is "+x);
/* Or do what ever you wish */
}
/*Remember to replace the yourfunctionname with your function's name */
OR If it is in a form try this:
Your form should look like this:
<form method="/* method */" action="/* action */" onSubmit="yourfunctionname()">
<input type="search" name="search" id="searchid"/>
/* Rest of your form*/
</form>
Javascript:
document.getElementById("searchid").value = localStorage.getItem("saved");
document.getElementById("searchid").onsearch = function() {yourfunctionname()};
/* Put this before the below function or in the top of the document */
function yourfunctionname(){
var x = document.getElementById("searchid").value;
alert("The current value of the search field is "+x);
/* Or do what ever you wish */
/* The below code does the trick*/
localStorage.setItem("saved", x);
location.reload();
return false;
}
/* Remember to replace the yourfunctionname with your function's name */
If you are having a different function to submit the form then replace your form's onsubmit attribute with that function's name and a word "return" before it's name and add the below javascript inside that function.
var x = document.getElementById("searchid").value
localStorage.setItem("saved", x);
location.reload();
return false;
If you wanted something else then please comment.
Please accept as if it solves your problem.
And thanks...
Lastly for more just comment

Need to run a function based on conditional

I'm trying to assign a function to a couple of checkboxes, but I only want them added based on a condition, in this case the step number of the form. This is a roundabout way of making the checkboxes readOnly AFTER they have been selected (or not). So, at step 1 I want the user to choose cb1 or cb2, but at step 2 I want to assign the function that will not let the checkboxes values be changed.
What am I doing wrong?
function functionOne() {
this.checked = !this.checked
};
if (document.getElementById("stepNumber").value == 2) {
document.getElementById("cb1").setAttribute("onkeydown", "functionOne(this)");
document.getElementById("cb2").setAttribute("onkeydown", "functionOne(this)");
}
You are passing the element in an argument, so use that:
function functionOne(elem) {
elem.checked = !elem.checked
};
You could also use properties:
document.getElementById("cb1").onkeydown = functionOne;
document.getElementById("cb2").onkeydown = functionOne;
function functionOne() {
this.checked = !this.checked
};
This is a solution that requires jquery but you can use the .click function to disable checkboxes once one is clicked.
Here is a working example:
http://jsfiddle.net/uPsm7/
Why not on the selection disable the checkbox?
Function onCheck(elm)
{
document.getElementById("cbValue").value = elm.value;
elm.disabled = true;
}
<input id="cbValue" type="hidden" />
Use the hidden input field to allow form to send data back to server.

Javascript Bind on Blur, Both 'if indexOf' and 'else' Performed

HTML
<!-- Contents of div #1 -->
<form id="6hgj3y537y2biacb">
<label for="product_calendar" class="entry_label">Calendar</label>
<input type="text" name="product_calendar" class="entry" value="" />
</form>
<form id="pyc2w1fs47mbojez">
<label for="product_calendar" class="entry_label">Calendar</label>
<input type="text" name="product_calendar" class="entry" value="" />
</form>
<form id="kcmyeng53wvv29pa">
<label for="product_calendar" class="entry_label">Calendar</label>
<input type="text" name="product_calendar" class="entry" value="" />
</form>
<!-- Contents of div #2 -->
<div id="calendar_addRemove"> <!-- CSS >> display: none; -->
<div id="calendar_add">
<label for="calendar_add" class="calendar_addLabel">Add Occurrence</label>
<input type="text" name="calendar_add" class="calendar_addInput" value=""/>
</div>
<div id="calendar_remove">
<label for="calendar_remove" class="calendar_removeLabel">Remove Occurrence</label>
<input type="text" name="calendar_remove" class="calendar_removeInput" value=""/>
</div>
</div>
Javascript
// Complete behavioral script
$(function() {
$('input[name=product_calendar]').css({ 'color': '#5fd27d', 'cursor': 'pointer' }).attr({ 'readonly': 'readonly' }); // Additional formatting for specified fields
$('input[name=product_calendar]').focus(function() { // Focus on any 'input[name=product_calendar]' executes function
var product_calendar = $(this); // Explicit declaration
var attr_val = $(product_calendar).attr('value');
$('#calendar_addRemove input').attr({ 'value': '' }); // Clear input fields
$('#calendar_addRemove').fadeIn(500, function() { // Display input fields
$('input[name=calendar_add]').blur(function() { // After value entered, action occurs on blur
alert('Blur'); // Added for testing
var add_val = $('input[name=calendar_add]').attr('value');
if (add_val != '') {
alert('Not Blank'); // Added for testing
var newAdd_val = attr_val + ' ' + add_val;
$(product_calendar).attr({ 'value': newAdd_val });
$('#calendar_addRemove').fadeOut(500);
}
else {
alert('Blank'); // Added for testing
$('#calendar_addRemove').fadeOut(500);
}
});
$('input[name=calendar_remove]').blur(function() { // After value entered, action occurs on blur
alert('Blur'); // Added for testing
var remove_val = $(this).attr('value');
if (remove_val != '') {
alert('Not Blank'); // Added for testing
if (attr_val.indexOf(remove_val) != -1) {
alert('Eval True'); // Added for testing
var newRemove_val = attr_val.replace(remove_val, '');
$(product_calendar).attr({ 'value': newRemove_val });
$('#calendar_addRemove').fadeOut(500);
}
else {
alert('Eval False'); // Added for testing
$('#calendar_remove').append('<p class="error">Occurrence Not Found</p>');
$('.error').fadeOut(1500, function() { $(this).remove(); });
}
}
else {
alert('Blank'); // Added for testing
$('#calendar_addRemove').fadeOut(500);
}
});
});
});
});
I've added a few alerts to see the order this script is performing in. When I enter 1234 into input[name=calendar_add] and blur, the alerts come up as expected. Then, when I proceed and enter 1234 into input[name=calendar_remove] and blur, this script throws up alerts in the following order: Blur, Not Blank, Eval False, Blur, Not Blank, Eval True - If I repeat this process, the occurrence of my alerts double every time (both add and remove), however keeping the same order (as if in sets).
I think the issue is multiple value re-declaration of the variable attr_val in the DOM, but I'm not quite sure how to revise my script to alleviate this issue.
It doesn't. That is not possible.
So, there are some possible reasons that it might seem so:
The code that actually runs doesn't look like that. It might be an older version that is cached, or you are looking in the wrong file.
The code runs more than once, that way both execution branches may run. (Although I can't really see any possibility for that here.)
You are misinterpreting the result, and whatever you see that leads to the conclusion that both branches have to be executed, is in fact caused by some other code.
You could use a debugger to set breakpoints in the code. Set one breakpoint before the condition, and one in each branch. Then you will see if the code runs twice, once or not at all.
Edit:
The alerts that you added to the code shows that the event is actually called twice, and the first time the values are not what you think that they are.
Add some code to try to find out where the event is invoked from. Catch the event object by adding it to the function signature: .blur(function(e) {. Then you can use e.currentTarget to get the element that triggered the event, and display some attributes from it (like it's id) to identify it.
Edit 2:
I am curios about this line:
$(product_calendar).attr({ value: newRemove_val });
Do you create the variable product_calendar somewhere, or did you mean:
$('input[name=product_calendar}').attr({ value: newRemove_val });
Edit 3:
Seeing the complete code, the cause of the double execution is clear. You are adding even handlers inside an event handler, which means that another handler is added every time.
The reason for attr_val not working properly is because it's created as a local variable in one function, and then unsed in another function.
Add the blur handlers from start instead, and they occur only once. Declare the variable outside the function.
Some notes:
You can use the val function instead of accessing the value attribute using the attr function.
When you assign $(this) to product_calendar, it's a jQuery object. You don't have to use $(product_calendar).
The removal doesn't match complete values, so you can add 12 and 2, then remove 2 and you get 1 and 2 left.
(this is a dummy text, because you can't have a code block following a list...)
// Complete behavioral script
$(function() {
// declare variables in outer scope
var attr_val;
var product_calendar;
$('input[name=product_calendar]')
.css({ 'color': '#5fd27d', 'cursor': 'pointer' })
.attr('readonly', 'readonly') // Additional formatting for specified fields
.focus(function() { // Focus on any 'input[name=product_calendar]' executes function
product_calendar = $(this); // Explicit declaration
attr_val = product_calendar.val();
$('#calendar_addRemove input').val(''); // Clear input fields
$('#calendar_addRemove').fadeIn(500); // Display input fields
});
$('input[name=calendar_add]').blur(function() { // After value entered, action occurs on blur
var add_val = $(this).val();
if (add_val != '') {
product_calendar.val(attr_val + ' ' + add_val);
}
$('#calendar_addRemove').fadeOut(500);
});
$('input[name=calendar_remove]').blur(function() { // After value entered, action occurs on blur
var remove_val = $(this).val();
if (remove_val != '') {
if (attr_val.indexOf(remove_val) != -1) {
product_calendar.val(attr_val.replace(remove_val, ''));
$('#calendar_addRemove').fadeOut(500);
} else {
$('#calendar_remove').append('<p class="error">Occurrence Not Found</p>');
$('.error').fadeOut(1500, function() { $(this).remove(); });
}
} else {
$('#calendar_addRemove').fadeOut(500);
}
});
});
OK, I think I understand the issue now.
Every time you do a focus on the product_calendar elements, you do a fadeIn on the #calendar_addRemove element. Every time you do that fadeIn, you use its callback to bind new blur handlers to the calendar_add and calendar_remove elements. That means that over time, those elements will have multiple blur handlers (all executing the same logic.) That can't be what you want.
In the script below, I've pulled out the nested handlers so that they're only bound once to each element. Note that:
product_calendar is declared (as null) at the top of the anonymous function, and then updated by the focus handler on the product_calendar element. I think this results in correct behavior.
attr_val is declared and assigned locally in both of the blur handlers. Again, I think this results in the correct behavior: If you were to declare it outside of the blur handlers (as product_calendar is declared), then you might accidentally use old values when you access it.
I'm still not sure exactly how this code is supposed to function, but this script performs in a way that I'd consider "reasonable".
(By the way, production code should probably allow for whitespace at the beginning and end of the input strings.)
$(function() {
var product_calendar = null;
$('input[name=product_calendar]').css({ 'color': '#5fd27d', 'cursor': 'pointer' }).attr({ 'readonly': 'readonly' }); // Additional formatting for specified fields
$('input[name=calendar_add]').blur(function() { // After value entered, action occurs on blur
alert('Blur'); // Added for testing
var add_val = $('input[name=calendar_add]').attr('value');
if (add_val != '') {
alert('Not Blank'); // Added for testing
var attr_val = $(product_calendar).attr('value');
var newAdd_val = attr_val + ' ' + add_val;
$(product_calendar).attr({ 'value': newAdd_val });
$('#calendar_addRemove').fadeOut(500);
}
else {
alert('Blank'); // Added for testing
$('#calendar_addRemove').fadeOut(500);
}
});
$('input[name=calendar_remove]').blur(function() { // After value entered, action occurs on blur
alert('Blur'); // Added for testing
var remove_val = $(this).attr('value');
if (remove_val != '') {
alert('Not Blank'); // Added for testing
var attr_val = $(product_calendar).attr('value');
if (attr_val.indexOf(remove_val) != -1) {
alert('Eval True'); // Added for testing
var newRemove_val = attr_val.replace(remove_val, '');
$(product_calendar).attr({ 'value': newRemove_val });
$('#calendar_addRemove').fadeOut(500);
}
else {
alert('Eval False'); // Added for testing
$('#calendar_remove').after('<p class="error">Occurrence Not Found</p>');
$('.error').fadeOut(1500, function() { $(this).remove(); });
}
}
else {
alert('Blank'); // Added for testing
$('#calendar_addRemove').fadeOut(500);
}
});
$('input[name=product_calendar]').focus(function() { // Focus on any 'input[name=product_calendar]' executes function
product_calendar = $(this);
$('#calendar_addRemove input').attr({ 'value': '' }); // Clear input fields
$('#calendar_addRemove').fadeIn(500);
});
});

Categories

Resources