Perform a tab with js

Ah okay. That makes sense, I guess i am so use to making divs that I did’nt consider the other possibilities :lol:

I will probably go with one of those and of those the input. Just to make things easier as we have the looping js coming up and i believe <button> were designed for input type=“button” /> that needed a line-break inside it; and we don’t.

Okay, I changed it. And now there are no errors and onClock of #tab it alerts, but it alerts undefined. Every time.

:confused: The alert is just this: alert(previousFocus.name); and previousFocus was defined right above it— var form = this.form; previousFocus = form.previousFocus;

Any ideas? or suggestions?

If you haven’t clicked on some other form field before hitting the tab button, there won’t be any form field that has had a previous focus, so getting undefined in that situation is expected.

How do things go when you click on a form field and then click on the tab button?

Oh Duh (Дa!)* :lol:

Yes, also (Дa!)*! It works— alerts the name of the field that was focused on and then blurs it.

*Those were russian jokes.
[OFF TOPIC]
The American-english phrase “Duh!”— meaning obviously / how stupid of me / no **** !— was invented during the Cold-War to mock the Russian word for yes, which is pronounced “dah”. Hence the similarity. In the cyrilic alphabet dah / yes in russian in spelled Дa.
So I meant it as the phrase Duh and later as yes.
[/OFF TOPIC]

Okay, lets move on to the loop stuff.

You can get rid of that alert now, and we can work on the loop part.

How it will work is that we’ll loop through all of the :input elements of the form, and when we get to the one that matches the previousFocus we’ll raise a flag called focusNext to say “Look! We found it! Whichever one we come across next is what we want to focus!”

The loop can then before checking the above stuff, check that flag to see if it’s raised, and if it is we know that we want to set the focus on that field, and then stop looping over all the others.

We can replace that alert with a variable declaration for focusNext


var focusNext = false;

The form controls that we want to loop through are the ones that match :input in the form, except for the tab button
$(‘:input:not(#tab), form’)

In fact, we can make that selector more flexible by excluding the element that triggered this click event (that in this case being the tab button), with:
$($(‘:input:not(#’ + this.id + ‘)’, form’)

So you can now use the .each() method to loop through those form controls, and inside that function you can check if the this keyword (which inside of the function for the each() method will be each of the form controls) with the previousFocus value. If they match you can set focusNext to true, and then for testing purposes alert that focusNext value.

Is this too much for this step? Or should we break it down in to smaller steps that are easier to understand and test?

Thank you for caring. I sincerely appreciate it :).
I don’t think so. At first it seemed like it, but I read it all first to get a grasp of the final result and then read and tried each part individually.
I think, or to me it seems that, I did it right. But it, the looping or alerting, is not working…

Is there supposed to be two “$(”, so “$($(” before the trigger of the .each() function?
Because now my syntax-highlighting is off?

Could that be the reason why it is not working or is it something I did in the loop with .each()

Here is just the js so far:


$(document).ready(function(){
    $('#sampleForm').prop('previousFocus', false);
    $('#sampleForm :input:not(#tab)').blur(function() {
        $(this.form).prop('previousFocus', this);
    });
    $('#tab').click(function(){
        var form = this.form;
        previousFocus = form.previousFocus;
        var focusNext = false;
        $($(':input:not(#' + this.id + ')', form')).each(function(){
        	if(this.prop() = 'previousFocus'){
        		focusNext=true;
        		alert(focusNext);
        	}
        });
    });
});

You’re right about not needing the double up there. it’s the rogue quote just after the word “form” which is throwing things off.

Could that be the reason why it is not working or is it something I did in the loop with .each()

Inside of the function you don’t need to get a property from the this keyword. The this keyword is a reference to a form element, and the previousFocus variable contains a reference to a form element, so just a straight comparison between the two is all that’s needed.

Don’t put previousFocus in quotes either, because that then turns it in to a string will will have nothing to do with what’s stored in the variable of the same name.

Also, the equals sign doesn’t do any comparing, that is an assignment operator instead. Use === to perform comparisons.

okay.

Oh okay. I put in quotse so that before once in got the value of the property of this, it could compare it to the string ‘previousFocus’, which would have been the value, right?

oh whoops. sorry. Quick and off-topic question, whats the difference between == and === in js?

Hmmm, i got rid of the extra $( and made sure all parts are closed. But the highlighting is still off and its not working and giving an Unexpected token error.


$(document).ready(function(){
    $('#sampleForm').prop('previousFocus', false);
    $('#sampleForm :input:not(#tab)').blur(function() {
        $(this.form).prop('previousFocus', this);
    });
    $('#tab').click(function(){
        var form = this.form;
        previousFocus = form.previousFocus;
        var focusNext = false;
        $(':input:not(#' + this.id + ')', form').each(function(){
        	if(this === previousFocus){
        		focusNext=true;
        		alert(focusNext);
        	}
        });
    });
});

':input:not(#' + this.id + ')', form'.each(function(){

oh, so no $( at all?But that doesn’t make any sense. How can the trigger or the caller of the function not be in the jQuery object $(…) format?

rouge quote?.. so should that line be:

$(':input:not(#' + this.id + ')', form).each(function(){

, but then the quotes are not closed…

Besides that and us finding the illegal token, do the other changes look right?

Earlier when the property was being set, ‘previousFocus’ was passed to the prop() method to tell jQuery to set that particular property.
Then in the click method we used assigned form.previousFocus to a variable called previousFocus retrieve the value that had been set. We could have also used the more long-winded $(form).prop(‘previousFocus’) to retrieve the same value.

Let’s get rid of the confusion, and not use prop() at all.

Instead of this:


$(this.form).prop('previousFocus', this);

Replace that with this:


this.form.previousFocus = this;

You should also get rid of that other prop() line that sets things to false.

The === comparison operator checks if two things are equal.
The == comparison operator checks if each side is truthy or falsie. It’s a much looser comparison that allows many bugs and problems to creep through.

Rouge is French for red. The rogue quote is the one that you removed. What causes you think that the quotes are not closed?

Let’s try it out and make further progress from there.

okay. thanks for explaining that. :slight_smile:

ahh okay. idk why I thought the quotes were not closed. Hmm, I guess i forgot the quotation-mark after the parentheses and before the comma :blush:

Okay, not it echo’s true once I press the tab-button…
This sounds correct because that means the if-statement’s conditions are met and focusNext is being set to true.

Good, so now that we have that flag, we can check within the same each method, but before the if statement, if the focusNext variable is true. If it is, it would have been the previous loop through which set it to be true. That’s why checking that focusNext is true must be before the if statement that checks the previousFocus. If the focusNext check was after, it would activate too soon.

So when the focusNext variable is true, you want to set that element to be the newly focused element, which can be done with this.focus()
After setting the focus you should also set focusNext to false, and you can then return false from the function to tell the loop to not carry on checking any others from there.

That will work in most situations.

There are some situations though that need to be fixed after that, such as when the last form field was the active one, or when nothing in the form has been made active. We’ll cover those after getting the focus part actually going.

Okay, I made the changes and it kind of works! :weee: :slight_smile:

But, first, did I put the return and do the things you wrote about in the previous post correctly?


<!Doctype HTML>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<title>tab button js</title>
		<style type="text/css">
		input, textarea, select{
	margin:5px 0;
	padding:5px;
	background:#f5f5f5;
	border:1px solid #d9d9d9;
	border-top:1px solid #c0c0c0;
	color:#000; /*Makes text black after user has typed it in. 
	In other words, when there is text in the input-field. This 
	makes it easier to read on the grey background after done typing */
	font:1em "Lucida Grande", "Lucida Sans Unicode", Arial, sans-serif;
	font-weight:bold;
	border-radius:5px;
	line-height:normal;
}   
input:hover, textarea:hover, select:hover{
	background:#fff;
	border:1px solid #b9b9b9;
	border-top:1px solid #a0a0a0;
	box-shadow:inset 0 1px 2px rgba(0,0,0,0.3);
	color:#000;
	font-weight:bold;
}
input:focus, textarea:focus{
	background:#fff;
	border:1px solid #4d90fe;
	box-shadow:inset 0 1px 2px rgba(0,0,0,0.3);
	color:#333;
	font-weight:normal;
	outline:none;
}
select:focus{
	box-shadow:inset 0 1px 2px rgba(0,0,0,0.3);
	color:#000;
}
#tab{
	height:35px;
	width:100px;
	border:1px solid #3079ed;
	background:#4d90f3;
	border-radius:3px;
	box-shadow:none;
	color:#fff;
	cursor:pointer;
	text-shadow:0 1px rgba(0,0,0,0.1);
}
#tab:hover{
	background:#357ae8;
	border:1px solid #2f5bb7;
	box-shadow:none;
	text-shadow:0 1px rgba(0,0,0,0.3);
}
#tab:active{
	box-shadow:inset 0 1px 2px rgba(0,0,0,0.3);
}
label{
	font-size:20px;
	font-weight:500;
}
		</style>
		<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
		<script type="text/javascript">
		$(document).ready(function(){
    $('#sampleForm').prop('previousFocus', false);
    $('#sampleForm :input:not(#tab)').blur(function() {
        $(this.form).prop('previousFocus', this);
        //this.form.previousFocus = this;
    });
    $('#tab').click(function(){
        var form = this.form;
        previousFocus = form.previousFocus;
        var focusNext = false;
        $(':input:not(#' + this.id + ')', form).each(function(){
        	if(focusNext === true){
        		this.focus();
        		focusNext=true;
        	}
        	if(this === previousFocus){
        		focusNext=true;
        		alert(focusNext);
        	}
        	return focusNext;
        });
    });
});
		</script>
	</head>
	<body>
            <legend align="center">Form</legend>
            <form  method="post" action="" id="sampleForm">

			<label for=name accesskey=U class="verticallyCentredInput"><span class="required">&#x2a;</span> Your Name: </label>
            <input name="name" type="text" id="name" size="28" autocomplete="off" />

			<br />
            <label for=email accesskey=E class="verticallyCentredInput"><span class="required">&#x2a;</span> Email: </label>
            <input name="email" type="text" id="email" size="28" autocomplete="off"  />

			<br />
          
            <label for=subject accesskey=S id="subjectLabel"><span class="required">&#x2a;</span> Subject: </label>
            <select name="subject" type="text" id="subject">
            	<option value="commt">Comments</option>
            	<option value="concr">Concerns</option>
            	<option value="bugs">Report a bug (issue)</option>
            	<option value="sales">Sales</option>
            	<option value="suppt">Support</option>
            </select>

			<br />
            <label for=comments accesskey=C><span class="required">&#x2a;</span> Your Comments: </label>
            <textarea name="comments" cols="27" rows="3" id="comments"></textarea>
            <hr />
            
            <input type="button" id="tab" value="Tab">

            </form>
        </body>
    </html>

By semi-working, 1.) it still alerts the value of focusNext, which I just have to remove.
[B]But, more importantly, when I manually focus on the first input and then click tab, it alerts true and then moves to the last input, not the email one…

Also, as you mentioned above, it does nothing if no input is focused on manually or if the last one is focused on, but first what can we do to get the order correct— meaning that once the tab button is clicked, it moves to the next form element (input, select, or textarea) ?[/B]

Not quite. You need to set focusNext to false instead of true, which will become useful later on.
Also, that return statement needs to be moved immediately after setting focusNext to false.
It also needs to be the value of false that is returned. There is no link between the value of focusNext and the return value, other than they being the same value, so don’t provide the false impression that they are related - just return false.

That last input issue will be sorted out once you get the focusNext value fixed, along with the return statement.

oh yeah, you said that. Thats my mistake, obviously, sorry about that.

oh okay. I didn’t know that, but know I do and I changed it. Just for curiosity sake, is there a reason why one shouldn’t return focusNext ? Or, is it just that we are not returning focusNext and that focusNext has nothing to do with the return— just that they share the same value? (as you mentioned, above)

hmm i think i fixed those two, but now after I focus on an input and then click the tab-button, the input is just blurred and nothing else— meaning nothing is focused on.

I hope I didn’t make another stupid mistake :lol: :blush:
Here is the code, did I do anything wrong ?:


$(document).ready(function(){
    $('#sampleForm').prop('previousFocus', false);
    $('#sampleForm :input:not(#tab)').blur(function() {
        $(this.form).prop('previousFocus', this);
    });
    $('#tab').click(function(){
        var form = this.form;
        previousFocus = form.previousFocus;
        var focusNext = false;
        $(':input:not(#' + this.id + ')', form).each(function(){
        	if(focusNext === true){
        		this.focus();
        		focusNext=true;
        	}
        	if(this === previousFocus){
        		focusNext=false;
        		return false;
        		//alert(focusNext);
        	}

        });
    });
});

That’s right. The purpose of returning false is to tell the each method to not process any more elements. That is not determined by the focusNext variable, or dependent on it.

Yes indeed. The code in the if statement for when it matches previousNext needs to be in the if statement for when focusNext is found, and the assignment for when focusNext is found needs to be in the previousNxt part.

really? I swear thats how it always was, the way I posted the code thus far in pervious posts.

Anywho, I changed it and now it is doing something— it is focusing on the form that was already manually focused on. Not the next one.

But that makes sense because i say focus on the thing that had previousFocus in the second if statement, yet you said switch the two suites, or what was inside the statements, wow… I am confused :confused:

Do you understand? :lol:

Show us what’s happening, and we’ll find the logical faults that are occurring.

Okay, I uploaded it here.

Tabbing with the keyboard-button in this webpage works normally; however, pressing the #tab button we made jumps to the last input form when nothing is focused on.
But when an input in focused on and then the button is pressed, it flashes and looks like it re-focuses on the same form.

So you see it?

The this.focusNext() part is in the wrong place. That need to be moved back to the start of the focusNext if statement.

there is no .focusNext() function, but I assumed you meant .focus() and I did so and it works!

I uploaded it to the same place as before if you want to check the syntax out.

Although I think this taught me as you explained to me what the pieces thus far do, when I was switching them around, I should have read the lines and interpreted what each one does; and therefore, mentally determine what if-statement it belongs in. In other words, I have to actually think and use my brain :lol:

So then now do we go to the pieces mention in post 70? :

Meaning looping back to the first form after the last one in focused on and focusing on the first if nothing is focused on?

Okay, we’ll just get some tidying up done with the variables, and then move on to resolving some issues.


var form = this.form;
previousFocus = form.previousFocus;
var focusNext = false;

Currently previousFocus is being defined as a global variable, which should not be done. You could define it as a var like this,


var form = this.form;
var previousFocus = form.previousFocus;
var focusNext = false;

but now we have three var declarations, which can lead to some confusion about where and when such var declarations should be made. Let’s help to remove confusion and declare the variables from one var declaration at the start of the function:

It’s possible to declare them elsewhere, but JavaScript hoists all declarations to the start of the function, automatically declaring them as undefined until it gets to the declaration that you made, so remove all confusion and declare them all at the same time at the start of the function.


var form = this.form,
    previousFocus = form.previousFocus,
    focusNext = false;

That’s how JavaScript variables should be declared. All at the same time at the start of the function they are in.

Let’s now work on getting the tab to loop. When the last form field has the focus, clicking on the tab button means that the focusNext variable is set to true, but the loop doesn’t go back to the start, thus allowing focusNext to be do it’s job.

What we can do is after the loop we can check if focusNext is still true, and if it is you can set the focus on to the first field of the form.

You can get the first element of the form with $(‘:input:first’, form)