Loop a jQuery selection in order of tab-index - javascript

So I have a varying amount of form inputs, and depending on how the app is set up in the CMS, they can be on different 'pages' (just show/hide pages within the same document). Which means that their tab-index doesn't necessarily follow the DOM structure.
They're also varying form types.
How would I go about looping through (validating) in order of the tab-index?
(note: the tab-index doesn't always follow an incremental pattern, as the 'next' button on one of the show/hide buttons also has a tab-index)
I have thought about something like this:
var $inputs = $('input[type="text"], select, input[type="radio"]'),
numInputs = $inputs.length,
numInputsChecked = 0,
tabIndex = 0;
while(numInputs != numInputsChecked){
var $input = $inputs.filter(function(){
return $(this).attr("tabindex") == tabIndex;
});
if($input.length){
// Do validation code
numInputsChecked++;
}
tabIndex++;
}
but I believe there should be a better way of achieving this task. (note, I haven't actually tested this code, I'm just attempting to illustrate what I am thinking)

This approach will do it, but there may be a more elegant way (I didn't put much time into this):
HTML:
<input type="text" tabindex="2" />
<select tabindex="4"></select>
<input type="text" tabindex="1" />
<input type="text" tabindex="3" />
JS:
/**
* Sort arrays of objects by their property names
* #param {String} propName
* #param {Boolean} descending
*/
Array.prototype.sortByObjectProperty = function(propName, descending){
return this.sort(function(a, b){
if (typeof b[propName] == 'number' && typeof a[propName] == 'number') {
return (descending) ? b[propName] - a[propName] : a[propName] - b[propName];
} else if (typeof b[propName] == 'string' && typeof a[propName] == 'string') {
return (descending) ? b[propName] > a[propName] : a[propName] > b[propName];
} else {
return this;
}
});
};
$(function(){
var elms = [];
$('input, select').each(function(){
elms.push({
elm: $(this),
tabindex: parseInt($(this).attr('tabindex'))
})
});
elms.sortByObjectProperty('tabindex');
for (var i = 0; i < elms.length; i++) {
var $elm = elms[i].elm;
console.log($elm.attr('tabindex'));
}
});

jQuery selectors return an array of elements in DOM order by default. See http://docs.jquery.com/Release%3AjQuery_1.3.2.
However, you could add custom selector behaviour by extending jQuery's default selector, see: http://james.padolsey.com/javascript/extending-jquerys-selector-capabilities/,
sorting your array of selected inputs using a technique similar to this plugin. Remember to ignore the parts that actually rearrange the elements.

As said, sorting objects is kind of none-sense ; Anyway, still you can use Array prototype. ...
Does your needs worth writting all that code ?
What do you mean by "better way " ?
-> choose the less greedy method (functions calls, caching objects etc..)
I think your code is okey, may be you could optimize your code and get ride off checked inputs :
var $input = $inputs.filter(function(){
return $(this).attr("tabindex") == tabIndex;
});
// Preventing your code from processing this element upon next loop if any.
$inputs = $inputs.not($input);
But this really makes sense while processing on hudge node collections ...

Related

Generic function to find child elements without jQuery

I'm trying to write a generic set of functions so that I can check if children of a particular element, with a particular feature exist. Unfortunately, for this task I don't have jQuery available to me.
A function call example:
has_child_with_id(element, 'obj123');
I'm trying to keep it compatible with the maximum number of browsers, and I'm aiming to have some explicitly named functions to find by name, id, class etc.
I'm still new to the javascript module pattern, but would something like this be appropriate?:
var myfunctions = (function() {
var public_interface = {
// NAMED FUNCTION
has_child_with_id: function(el, n) {
return public_interface.has_child_with_('Id', el, n);
},
// GENERIC FUNCTION
has_child_with_: function(type, el, n) {
// Option 1 (querySelectorAll)
return typeof el['querySelectorAll'] === 'function' && el['querySelectorAll']('['+type+'="'+n+'"]').length > 0
// Option 2 (get a bunch of elements, doesn't work on forms)
|| typeof el['getElementsBy'+type] === 'function' && el['getElementsBy'+type](n).length > 0
// Option 3 (get a single element)
|| typeof el['getElementBy'+type] === 'function' && typeof el['getElementBy'+type](n) !== 'undefined'
// Option 4 (Manually loop through elements)
|| (function(children, n) {
for (var i=0;i<children.length;i++) {
if (children[i].hasOwnProperty(type) && children[i][type] == n)
return true;
}
})(el.getElementsByTagName('*', n));
}
};
return public_interface;
})();
alert(myfunctions.has_child_with_id(document, 'myid'));
document.querySelector actually fits the bill here nicely. It's IE8+ compatible (IE9 if you're using CSS3 selectors). Rather than searching the document, simply prefix with the element you want to search within.
Get the first element that matches a class, ID, or data attribute.
var elem = document.querySelector('#some-element');
var firstMatch = elem.querySelector('.sample-class');
Get all elements that match a class, ID, or data attribute.
var elem = document.querySelector('#some-element');
var allMatches = elem.querySelectorAll('.sample-class');
In this case, firstMatch and allMatches are the ones you're looking for.
You could also search by ID, data attribute, or another other valid CSS selector:
elem.querySelectorAll('[data-something]'); // Matches by data attribute
elem.querySelectorAll('input[type="checkbox"]'); // Returns all checkboxes
querySelectorAll will return an array of nodes that you can loop through.

Javascript Math Functions

How can I use these JavaScript math functions ?
For example, I want to compute the square of all <input> values in a form, without submiting the form.
Can you give a little example? Thank you.
JQuery doesn't need to support math functions as it is an addon library for Javascript, you can still use Javascript in your JQuery code, so you can still use all the native math functions.
Examples:
Addition
var x = 1;
var y = 2;
var lol = x+y;
alert(lol);
Subtraction
var x = 10;
var y = 1;
var lol = x-y;
alert(lol);
Edit: Now we understand your question a little better...
<input type="text" id="field1" value="16" />
<input type="text" id="field2" value="25" />
<input type="text" id="field3" value="36" />
var field1Value = document.getElementById("field1").value;
var field2Value = document.getElementById("field2").value;
var field3Value = document.getElementById("field3").value;
alert(Math.sqrt(field1Value ));
alert(Math.PI * field2Value);
alert(Math.sin(field3Value));
You can act on each individual input using an each()(docs) loop.
Click here to test a working example. (jsFiddle)
$('a.square').click(function() {
$('#myform :text').each(function() {
this.value *= this.value;
});
});
$('a.square_root').click(function() {
$('#myform :text').each(function() {
this.value = Math.sqrt(this.value);
});
});
When either link is clicked, it finds all the text inputs in myform and iterates over them.
Inside the each function, this refers to the current input element.
JavaScript is the programming language, not jQuery, which is a library for web application programming written in JavaScript. To effectively use jQuery, you need to know JavaScript.
It is, however, possible to use jQuery's functionality to easily work with multiple textboxes at once:
// Set each single-line textbox's value to the square
// of its numeric value, if its value is in fact a number.
$('input:text').each(function() {
var num = +this.value;
if(!isNaN(num)) {
this.value = num * num; // or Math.pow(num, 2)
}
});
It would be quite useful if jQuery had a reduce() function.
When dealing with lists of data, most functional languages, and indeed most traditional languages these days, have methods that perform a repetitive function over the entire list, taking each element in turn and applying a function to it.
The simplest of these is map, which jQuery implements for you. This takes a list and applies a function to each element and returns the list of results, one result per entry in the list. eg. [1,2,3] -> (map x2) -> [2,4,6].
Sometimes you want a total or collective result from a list, rather than a list of individual mappings. This is where the reduce (or fold) operation comes in. Unfortunately jQuery does not have this method available as standard, so below is a plugin for it. A reduce function takes an accumulator value and the value of the current element, and returns the modified accumulator, which will be passed on to the next call. eg. [1,2,3,4] -> (reduce + [initial:0]) -> 10 = ( ( ( (0 + 1) + 2 ) + 3 ) + 4 ) or ([1,2,3,4] -> (reduce * [initial:1]) -> 24 = ( ( ( (1 * 1) * 2 ) * 3 ) * 4 ).
(function($) {
$.reduce = function(arr, callback, initial) {
var accumulator = initial || 0;
$.each(arr, function(index, value) {
accumulator = callback(accumulator, value, index);
});
return accumulator;
}
})(jQuery);
Then you can use it like this to get a sum of squares:
var answer = $.reduce($('input:text'), function(acc, elem) {
var cVal = $(elem).val();
return acc + cVal * cVal;
}, 0);
i was looking for a solution too , and i saw a lot of questions here that doesn't work (even this one) in case someone wondering like me , here is my working solutiuon :
$("#apport").keyup(
function(){
var apport = parseFloat($("#apport").val());
var montant = parseFloat($("#montant-financer").val());
var moinmontant = parseFloat(montant) - parseFloat(apport);
$("#montant-financer").val(moinmontant);
}
);
All the id's selector are input
Use the jquery map function to create an array
$('input:text').map(function() {
return this.value * this.value; // math calculation goes here
}).get();
See a live example
Looking at the initial question that was posted, it clearly states compute the square of all values in a form, without submiting the form.
i think keyup would be the best solution.
$("input").keyup(function () {
var value = $(this).val();
var x=value*value;
$("p").text(x);
}).keyup();
Click here to check the working example.
http://jsfiddle.net/informativejavascript/Sfdsj/3/
For more details visit http://informativejavascript.blogspot.nl/

remove element from array when checkbox unchecked

I add an element to an array when the checkbox is checked and I need to remove it when it is unchecked. I use splice to remove an element. I just can't seem to call an event when it's unchecked. I tried using this:
if ($('input[name="'+category+'"]:checked'))
item_id[category] = $(this).attr("id");
else
item_id.splice(category, 1);
It adds the needed element ok, when the checkbox is checked, but it doesn't seem to remove it when it's unchecked. Variable category is a loop variable and is correct.
If someone can work this out, it would be greatly appreciated.
jQuery selectors always return an object whether an element is matched or not.
What you've effectively got is:
if (new Object())
item_id[category] = $(this).attr("id");
else
item_id.splice(category, 1);
Objects are always truthy (no matter if it's an empty object, or an object John Resig initialized), so this if statement will never execute the else.
What you're probably after is:
if ($('input[name="'+category+'"]:checked').length)
item_id[category] = $(this).attr("id");
else
item_id.splice(category, 1);
Which checks the length property instead.
This still won't work however, as splice() will shift all elements in your array; making the category wrong.
If your binding the event on a number of checkbox elements, it will be unwise to use .bind() (and it's counterparts .click()), as this method will bind an event for each checkbox. Instead, use .live(), or .delegate(); this will bind one event to an ancestor of all checkbox elements, and listen for event (using JavaScripts event bubbling), which is much more efficient.
Taking both of these points into consideration, you might fancy something like this.
$(yourJquerySelector).live('change', function () {
var category = '?' // (this.value ?)
item_id[category] = this.checked ? this.id : undefined;
});
Not entirely sure what you are after to be honest, but here is my solution to this,hope it works for you in some way
Javascript Array - indexOf Method:
http://www.tutorialspoint.com/javascript/array_indexof.htm
<script>
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt /*, from*/)
{
var len = this.length;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
$(function() {
var checkedItems = new Array();
$(":checkbox").change(function () {
if($(this).attr('checked'))
{
checkedItems.push($(this).attr("id"));
}
else
{
var index = checkedItems.indexOf($(this).attr("id"));
checkedItems.splice(index,1);
}
});
});
</script>
HTML
<input type="checkbox" id="c1" value="1">
<input type="checkbox" id="c2" value="2">
<input type="checkbox" id="c3" value="3">
The splice function is meant to return what's removed, so start debugging by displaying its return value.
Change your if condition to:
$('input[name="'+category+'"]').is(':checked')
As mentioned by Matt, your current if condition is a selector that returns a list of jQuery elements. Testing the number of elements returned (using the length property) would also do the trick.

jQuery form zip code to state function

I'm trying to convert a preexisting javascript function into a jQuery function.
The function comes from http://javascript.internet.com/forms/zip-to-state.html
and aims to convert a user-entered zip code into a state. I'm using jQuery 1.3.2, with all the necessary plugins, but I'm not very familiar with jQuery syntax and how to convert this from plain ol' Javascript syntax.
The setState function takes two parameters, the zip code element and the state element, so I'm trying to do something like this:
$('$zip_code').change( function () { setState($(this), $('#state')); });
Any thoughts on this syntax? Thanks, Dakota
function getState(zip) {
if ((parseInt(zipString.substr(zip / 4, 1), 16) & Math.pow(2, zip % 4)) && (zip.length == 5))
for (var i = 0; i < stateRange.length; i += 7)
if (zip <= 1 * stateRange.substr(i, 5))
return stateRange.substr(i + 5, 2);
return null;
}
function setState(txtZip, optionBox) {
if (txtZip.value.length != 5 || isNaN(txtZip.value / 4)) {
optionBox.options[0].selected = true;
alert("Please enter a 5 digit, numeric zip code.");
return;
}
var state = getState(txtZip.value);
for (var i = 0; i < optionBox.options.length; i++)
if (optionBox.options[i].value == state)
return optionBox.options[i].selected = true;
for (var i = 0; i < optionBox.options.length; i++)
if (optionBox.options[i].value == "XX")
return optionBox.options[i].selected = true;
}
You can leave the getState and setState functions just as they are, but you should really read up on jQuery before you go asking questions on how to use it. If you don't even know the syntax, you might want to read up on how to use it.
Like Aberon said, use blur in this situation and change '$zip_code' to '#zip_code', where 'zip_code' corresponds to:
<input type="text" id="zip_code" name="whatever_you_want" />
You also want a select box with an id of 'state':
<select id="state">
... options ...
</select>
Aberon's solution still wont work, however, because it is incomplete...
$('#zip_code').blur( function () { setState($(this)[0], $('#state')[0]); });
You want to get the value from the zip_code input element, thus the val() call. You also want the DOM element for the select box with id == 'state', so instead of using the array returned by $('#state'), you want the first element.
Try it out, I hope this helped.
-Stephen
Unless your zip field is a drop down you would probably be better off using either blur or keyup in your function.
$('#zip_code').blur( function () { setState($(this), $('#state')); });
I also changed $zip_code to #zip_code.

How do I build a gracefully-degrading HTML5 Range?

I'd like to use the <input type='range' /> from HTML5 for browsers that support it and degrade to a <select /> if not. I'm using Ruby-on-Rails, so failing all else, I could do something like this on the server-side.
I would prefer, though, to have something more inline with the idea of progressive enhancement done via Javascript. Bonus points if it's JQuery.
Check out Modernizr, it will tell you if range is supported. I believe the technique is to create a range input and check it's type — if it is still "range" then it is supported. Otherwise it should report "text" which is the fallback in other browsers.
First detect if the browser can handle HTML 5 then use something like this:
$('input').each(function (i, item) {
if ($(item).attr('min') !== undefined && $(item).attr('max') !== undefined) {
var select = document.createElement("SELECT");
$(select).attr('name', $(item).attr('name'));
$(select).attr('id', $(item).attr('id'));
$(select).attr('disabled', $(item).attr('disabled'));
var step = 1;
if ($(item).attr('step') !== undefined) {
step = parseFloat($(item).attr('step'));
}
var min = parseFloat($(item).attr('min'));
var max = parseFloat($(item).attr('max'));
var selectedValue = $(item).attr('value');
for (var x = min; x <= max; x = x + step) {
var option = document.createElement("OPTION");
$(option).text(x).val(x);
if (x == selectedValue) { $(option).attr('selected', 'selected'); }
$(select).append(option);
};
$(item).after(select);
$(item).remove();
}
});
Since you can't use the input[type=range] selector i had to go with the $(item).attr('min') && $(item).attr('min') approach, this will get a little weird if you have other types of input controls with those two attributes.

Categories