How do I build a gracefully-degrading HTML5 Range? - javascript

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.

Related

How to get an element's left/top attribute in RAW JavaScript?

I created a codePen here (http://codepen.io/anon/pen/pwoEJ)
document.querySelector('#box').style.left
This line of code doesn't seem return the right value? It gives me "" empty string.
Use getComputedStyle:
document.defaultView.getComputedStyle(document.querySelector('#box')).left
try something like this,http://codepen.io/anon/pen/BjfGu
window.getComputedStyle(document.getElementById('box'),null).getPropertyValue('left');
It depends how cross-browser you want your code to be.
getComputedStyle is not available in IE8- (not sure if it works on IE9 either)
Also, the style might have been already set programmatically, in which case you will find the value inside the element.style object.
Finally, while getComputedStyle accepts CSS-like identifiers (e.g. z-index), the other ways of accessing style elements use camelized names (e.g. zIndex).
In the end, a more general solution is far from trivial:
getStyle = function getStyle (el, property)
{
var style;
if(document.defaultView && document.defaultView.getComputedStyle)
{
style = document.defaultView.getComputedStyle(el, "")
.getPropertyValue (property);
if (style) return style;
}
property = Camelize (property); // this is for older IE versions
if (el.currentStyle) style = el.currentStyle[property];
return style || el.style[property]
}
function Camelize (string)
{
var oStringList = string.split('-');
if (oStringList.length == 1) return oStringList[0];
var camelizedString = string.indexOf('-') == 0
? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
: oStringList[0];
for (var i = 1, len = oStringList.length; i < len; i++)
{
var s = oStringList[i];
camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
}
return camelizedString;
}
(this code adapted for readability from Bernard Sumption's excellent Animator.js library)

How can i know its valid in JS?

I am developing an Adobe interactive form with LiveCycle LC designer with JavaScript.
// Identify required fields (it may be a free text field, drop-down, check box, i mean there 3 kinds possibilties) and make yellow colored them
var myArrayYellow = new Array();
var yellowFields;
yellowFields = my_required_fields_list_string.rawValue
myArrayYellow = yellowFields.split(" ");
for (var i = 0; i < myArrayYellow.length; i++) {
===> Here at this point, i want to check the existence of [i] field in the form that its a valid field/objetc or not? bcz, i have chances of getting non-existing fields in the my_required_fields_list_string, hence prior to assigning yellow color to them, i want to check their validity on the form or not? Pls. let me know the JS for this // if its true/found, then only assign yellow color as below
xfa.resolveNode("MY_ADOBE_FORM.." + myArrayYellow [i]).ui.oneOfChild.border.fill.color.value = "255,255,254"
};
For some other purpose, some expert gave me a JS as below, i tried to tune it as per my above requirement, but its not working
function findNodes(vNode){
if (vNode.className === "field"){
if (vNode.isPropertySpecified("name") === true){
var myStateName = new RegExp(vNode.name);
var returnValue = GFL.search(myStateName);
if (returnValue != -1) {
this.ui.oneOfChild.border.fill.color.value = "192,192,192";
this.access = "readOnly";
} else {
this.ui.oneOfChild.border.fill.color.value = "255,255,255"; //whatever colour is open access
this.access = "open";
}
}
}
for (var a=0;a<vNode.nodes.length;a++) {
findNodes(vNode.nodes.item(a));
}
}
findNodes(xfa.form);
if I understand your problem, you need to check if xfa.resolveNode returns something and handle it from there.
var node;
if ( (node=xfa.resolveNode("MY_ADOBE_FORM.." + myArrayYellow[i]) )!==null){
node.ui.oneOfChild.border.fill.color.value = "255,255,254"
}
If I understand correctly, you are trying to check if all of your values in the array are valid before preforming operations on them. Just check and make sure they are not null.
EDIT: You should probably check for empty string as well.
for (var i = 0; i < myArrayYellow.length; i++) {
if (!(myArrayYellow[i] == null || myArrayYellow[i] == "")){
//Do Stuff
}
}

Loop a jQuery selection in order of tab-index

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 ...

Javascript performance issue with loop

We are having performance issues with that code.It works in loop with 150 times.
That code work for betting, matches have empty bet fields on screen.Then that code works for fill winner odds with looking and comparing scores.
In example, if match finished with 1-0 home team win, i must write "MS1" on screen.And for making that, i must get score info using jQuery attr selector.
In the weekends, there are a lot of matches and it is crashing or it works too slow :/
Have you any ideas to work faster?
OddEngine = function(odd)
{
$("#matchCode_" + odd.ID).html(odd.C);
$("#match_" + odd.ID).attr("code",odd.C);
var status = $("#match_" + odd.ID).attr("status");
if (status == 1)
return;
var htscore = $("#othomeTeamScore_"+odd.ID).html();
var atscore = $("#otawayTeamScore_"+odd.ID).html();
var iy_htscore = $("#homeTeamHalfScore_"+odd.ID).html();
var iy_atscore = $("#awayTeamHalfScore_"+odd.ID).html();
for (var i = 0; i < odd.Odds.length; i++) {
var bet = odd.Odds[i];
var winnerMsOdd = 'F.X';
var winnerMsTitle = 'X';
if (htscore > atscore)
{
winnerMsOdd = 'F.1';
winnerMsTitle = '1';
}
else if (htscore < atscore)
{
winnerMsOdd = 'F.2';
winnerMsTitle = '2';
}
$("#match_"+odd.ID+" [oddcode='MS']").html(bet[winnerMsOdd]);
$("#match_"+odd.ID+" [oddtag='MS']").fadeIn();
$("#match_"+odd.ID+" [oddtag='MS']").html(winnerMsTitle);
if (currentSportId != 3)
{
var winnerIyOdd = 'S.X';
var winnerIyTitle = 'X';
if (iy_htscore > iy_atscore)
{
winnerIyOdd = 'S.1';
winnerIyTitle = '1';
}
else if (iy_htscore < iy_atscore)
{
winnerIyOdd = 'S.2';
winnerIyTitle = '2';
}
if (bet[winnerIyOdd])
{
$("#match_"+odd.ID+" [oddcode='IY']").html(bet[winnerIyOdd]);
$("#match_"+odd.ID+" [oddtag='IY']").fadeIn();
$("#match_"+odd.ID+" [oddtag='IY']").html(winnerIyTitle);
}
}
if (currentSportId == 1)
{
var winnerAuOdd = 'UNDER';
if (parseInt(htscore) + parseInt(atscore) > 2.5)
{
winnerAuOdd = 'OVER';
}
if (bet[winnerAuOdd])
{
$("#match_"+odd.ID+" [oddcode='AU']").html(bet[winnerAuOdd]);
$("#match_"+odd.ID+" [oddtag='AU']").fadeIn();
$("#match_"+odd.ID+" [oddtag='AU']").html(winnerAuOdd == 'UNDER' ? 'ALT' : 'ÜST');
}
var winnerTGOdd = 'GS.01';
var winnerTGtitle = "0-1";
if (parseInt(htscore) + parseInt(atscore) > 1 && parseInt(htscore) + parseInt(atscore) < 4)
{
winnerTGOdd = 'GS.23';
winnerTGtitle = "2-3";
}
else if (parseInt(htscore) + parseInt(atscore) > 3 && parseInt(htscore) + parseInt(atscore) < 7)
{
winnerTGOdd = 'GS.46';
winnerTGtitle = "4-6";
}
else if (parseInt(htscore) + parseInt(atscore) >= 7)
{
winnerTGOdd = 'GS.7P';
winnerTGtitle = "7+";
}
if (bet[winnerTGOdd])
{
$("#match_"+odd.ID+" [oddcode='TG']").html(bet[winnerTGOdd]);
$("#match_"+odd.ID+" [oddtag='TG']").fadeIn();
$("#match_"+odd.ID+" [oddtag='TG']").html(winnerTGtitle);
}
}
}
$("#msOdd_" + odd.ID).html(odd.C);
if (currentSportId == 1 || currentSportId == 2 || currentSportId == 7)
{
$("#htOdd_" + odd.ID).html(odd.Odds["F.1"]);
}
$("#uOdd_" + odd.ID).html(odd.C);
$("#tOdd_" + odd.ID).html(odd.C);
}
There's a lot of bad stuff in there:
Issues:
You are hammering away at the DOM repeatedly. This is unavoidable but you don't need to do as much as you are doing. A lot of the remaining points follow-up on how to avoid.
You're using attribute selectors. These are slow and don't have native methods to support in most non-XML scenarios so are going to force a lot more work out of the interpreter. Try as classes instead. You can have multiple classes and add/remove with jQuery addClass and removeClass functions without interfering with other classes. If you're supporting IE8, narrow down to nearest ID and use a tag selector with a class.
You're not caching any of your JQ selectors. $('#someId') does a bit of work (although is faster than just about anything else. If it's going to get re-used, assign to a var. Convention is: var $someId = $('#someId'); so you know it's a jqObject. This repeatedly: $('#someId <other stuff>) is probably slower than this: $someId.find(<otherstuff>) repeatedly. In your case, assuming odd.id is unique, you'd at least want: var $matchHtml = $("#matchCode_" + odd.ID) at the top of the loop.
You're doing a ton of UI stuff as you go. Consider building the collections you need and then handling it all at once after the loop. e.g. building two JQ objects for AU and TG (see the 'add' method) and then hitting them with the functionality they need once the loop is complete.
This is probably less of a big deal than the JQ stuff but you're using a lot of '.' operators unnecessarily. Every '.' does actually represent some work and in some cases actually represent getters like length which can do a fair bit more work since they have to count up the array elements. Here's the hyper-anal loop which also has the nice side-effect of being more concise:
var myOdds = odd.Odds,
i=myOdds.length;
//if order matters, this goes backwards. Easy fix: myOdds.reverse
while(i--){
thisOdds = myOdds[i];//if you're using thisOdds more than once
//do stuff here
}
You could the '#match'+odd.ID node and key all your node searches within the loop from this node. eg. matchOdd.find( '[oddcode="MS"]' ), this should improve the performance against querying the DOM.
As for improving performance in the loop you could consider making it async by delegating to setTimeout. Here is a link to a resource that explains how to approach this http://www.kryogenix.org/days/2009/07/03/not-blocking-the-ui-in-tight-javascript-loops

Search attribute in JavaScript

Without using any open source framework (jQuery, etc.) :), in JavaScript, what's the most efficient way to search for attributes in any controls. (Any old/new browsers)
This is the pattern I'm kind of following. Is there any better way or any better getElementByAttribute() method? Thanks!
e.g
<input type="button" id="b1" value="Continue" a1="something" />
<input type="text" id="t1" a1="something1" />
<script>
var attrToFind = "something;something1";
var elems = document.all ? document.all : document.getElementByTagName("*");
//assume elems work always
for(var i = 0 ; i < elems.length; i++)
{
var att = elems[i].getAttribute("a1");
if (typeof att == "string")
{
if (att.indexOf(attrToFind) > -1)
... //search which attr you find, create array, save value, etc.
}
}
</script>
There is. Given that browser supports other means to collect elements, such as document.querySelectorAll (css expression) or document.evaluate (xpath expression), these "specialized" methods are usually more efficient.
document.querySelectorAll('*[foo="bar"]');
document.evaluate("//*[#foo='bar']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
Accessing an HTMLCollection (returned by getElement[s]By* functions) is slow in comparison to accessing arrays because an HTMLCollection must match the document at all times (it is live).
For this reason, it's best to create an array from the HTMLCollection and iterate over that.
This is a bit more optimized for speed:
var attrToFind = "something;something1",
elems = document.all ? document.all : document.getElementByTagName('*'),
i, attr;
// Works in Firefox; not sure about other browsers and engines.
elems = Array.prototype.slice.call(elems);
i = elems.length;
while(i --> 0) {
attr = elems[i].getAttribute('a1');
// Are you sure you want indexOf?
// att === attrToFind may be faster, as it requires one less comparison.
if(typeof att !== 'string' || att.indexOf(attrToFind) < 0) {
continue;
}
// Do stuff.
}
This is maybe the solution you'll need:
function $$$$(obj) {
var attrToFind = obj;
var elements = document.getElementsByTagName('*');
var attrResults = [];
var x = 0;
while (x < elements.length) {
var attr = elements[x].getAttribute('a1');
if (attr !== null) {
if (attr.indexOf(attrToFind) > -1) {
attrResults.push(elements[x]);
}
}
x++
}
return attrResults;
}
Run function:
$$$$('something');
Result is an Array with all elements with the class 'something'.
Maybe somebody can refactor my code, so that is working also with more than 1 parameter.
I hope I could help you.

Categories