Javascript equivalent of $('input[name$="value"]') in jQuery? - javascript

How can I get an input element whose name attribute ends with a particular value using just javascript?
I found this $('input[name$="value"]') method available in jQuery.
Is there a function like that available in javascript? I cannot use jQuery for some reason.

I'm surprised that nobody has mentioned this yet. Have you tried:
var elements = document.querySelectorAll('input[name$="value"]');
This will only work if the browser supports the querySelectorAll method but it is what jQuery uses underneath if support is found.
Here's the table that lists browser support for the method:
When can I use querySelector/querySelectorAll?

You can do this:
console.log(document.getElementsByName("value")[0]);
Or if you want to find the first input element with "value" in its name:
var searchString = "value";
var elems = document.getElementsByTagName('input'), wantedElem;
for(var i = 0, len = elems.length; i < len; i++) {
if(elems[i].name.lastIndexOf(searchString) == elems[i].name.length-searchString.length) {
wantedElem = elems[i];
break;
}
}
console.log(wantedElem);

I'm going to assume that you've placed these <input> elements where they belong (in a <form> element).
The easiest way to traverse form controls via the DOM API is via the HTMLFormElement::elements HTMLCollection. It allows named traversal, which is very handy for specific elements.
For example, consider the following markup:
<form action="./" method="post" name="test"
onsubmit="return false;">
<fieldset>
<legend>Test Controls</legend>
<input name="control_one" type="text"
value="One">
<input name="control_two" type="text"
value="Two">
<input name="control_three" type="text"
value="Three">
</fieldset>
</form>
Following that, some simple document tree traversal is required. Here's the entirety of it:
var test = document.forms.test,
controls = test.elements;
function traverseControl(control)
{
var patt = /one$/,
match = patt.test(control.name);
if (match) {
// do something
console.log(control);
}
}
function traverseControls(nodes)
{
var index;
if (typeof nodes === "object" &&
nodes.length) {
index = nodes.length - 1;
while (index > -1) {
traverseControl(
nodes[index]
);
index -= 1;
}
}
}
traverseControls(controls);
As you can see, it really isn't too difficult. The upshot of using HTMLCollections is the support of browsers old and new. Since HTMLCollections were implemented in DOM 0, they're widely supported.
In the future, I'd suggest using traversal that's far less vague. If you're in control of the document tree being traversed (i.e. you wrote the markup), you should already know the names of controls. Otherwise, vague approaches like the preceding must be used.
Working example: http://jsbin.com/epusow
For more, this article can be perused.

This method returns an array of elements
var elements = document.getElementsByName("value");

Related

picking out an element from a nodelist

I've just started to learn some basics of javascript so I'm still confused about several concepts.
I'm hoping some of you can kindly help me with this simple question about nodelist and everything.
I was trying to write code to make a program similar to 'wordle' and I got curious how the code below work well.
<input class="input">
<input class="input">
<input class="input">
<input class="input">
<input class="input">
<button>submit</button>
<script>
let answer = 'abcde';
document.querySelector('button').addEventListener('click', function () {
var input = document.querySelectorAll('.input');
for (let i = 0; i < 5; i++) {
if (input[i].value == answer[i]) {
input[i].style.background = 'green';
} else if (answer.includes(input[i].value)) {
input[i].style.background = 'yellow';
} else {
input[i].style.backgrouond = 'lightgrey';
}
</script>
I thought that querySelectorAll('.input') would return a nodelist composed of 5 input elements.
So I was going to put a code like input[0][0] to access the first character of the first string of the list, but without an extra [0] it worked fine.
That's what confuses me now.
And plus, can anyone tell me what .value does?
what is this property (or method maybe..?) doing here?
To get the first character of the first input value, you need to use the following:
const inputs = document.querySelectorAll('.input');
const firstInputValue = inputs[0].value;
const firstCharacter = firstInputValue[0];
After getting all the elements with class "input", you get the value of the first element in the NodeList and then take the first character of that input.
The value property is used to get or set the input value of the input field. Take a look at this for more information.

Wildcards in HTML5 data attributes

Is it possible to find all DOM elements with jQuery with wildcards in the attribute name?
Consider the following HTML:
<input
id="val1"
type="text"
data-validate-required
data-validate-minlength="3"
data-validate-email />
What I am trying to achieve is to find all dom nodes with an attribute name starting with data-validate-
As far as I understand the wildcards described here are concerned with "value" of the attribute.
The reason for this is - I want to find out which elements should be validated at all - and afterwards find out which validation parameters (like -email) comes in play.
Thanks
You can create a custom pseudoclass to e.g. match attribute names against a regexp: http://jsfiddle.net/hN6vx/.
jQuery.expr.pseudos.attr = $.expr.createPseudo(function(arg) {
var regexp = new RegExp(arg);
return function(elem) {
for(var i = 0; i < elem.attributes.length; i++) {
var attr = elem.attributes[i];
if(regexp.test(attr.name)) {
return true;
}
}
return false;
};
});
Usage:
$(":attr('^data-')")
Because JQuery relies heavily on XPath, and XPath does not support wildcard attribute selection - it is not possible without the overhead you're looking to avoid.
There's always the possibility of creating your own selector, just to keep things clean:
//adds the :dataValidate selector
$.extend($.expr[':'],{
dataValidate: function(obj){
var i,dataAttrs=$(obj).data()
for (i in dataAttrs) {
if (i.substr(0,8)=='validate') return true;
}
return false;
}
})
Which will allow you to use :dataValidate in your normal jQuery selectors:
$(".element:dataValidate .etc")
Working JSFiddle: http://jsfiddle.net/rZXZ3/
You could loop over the atributes:
$('.element').each(function() {
$.each(this.attributes, function(i, att){
if(att.name.indexOf('data-validate')==0){
console.log(att.name);
}
});
});
You can use filter method and dataset object:
Allows access, both in reading and writing mode, to all the custom data attributes (data-*) set on the element. It is a map of DOMString, one entry for each custom data attribute.
$("input").filter(function(){
var state = false;
for (i in this.dataset)
if (i.indexOf('validate') > -1) state = true;
return state
})​
http://jsfiddle.net/Pxpfa/

Custom attribute issue

I'm trying to get the "quantity" attribute of multiple Elements ID at the same time.
Here's what I tried :
productMinimum : document.getElementsById("id1","id2").getAttribute("quantity")
How can I make this work? The "id1" and "id2" elements all have the "quantity" attribute.
Here's what it looks like from the HTML side :
<li class="product" name="RD-101" id="id1" price="18" quantity="2">
<li class="product" name="RD-101" id="id2" price="18" quantity="4">
The problem you're having is that getElementsById() doesn't exist (unless you've defined it elsewhere). What you should be using is getElementById(), albeit twice (as getElementById(), as its name implies, returns only one element, even if there are multiple elements with that same id, which is invalid mark-up, so please don't do that), and then pushing the returned elements into an array:
var products = [];
products.push(document.getElementById("id1"));
products.push(document.getElementById("id2"));
You could, of course, create your own function to return multiple elements based on their id:
function getElementsById(ids) {
if (!ids) {
return false;
}
else {
var elems = [];
for (var i = 0, len = ids.length; i < len; i++) {
if (document.getElementById(ids[i])) {
elems.push(document.getElementById(ids[i]));
}
}
return elems;
}
}
console.log(getElementsById(['id1','id3']));​
JS Fiddle demo.
Bear in mind, though, that this returns a regular array, not a nodeList (as would be returned, for example, by getElementsByClassName()). And the returned array, even if it's only a single element, would have to be iterated over in the same way as any other array.
References:
getElementById().
nodeList.
push().
function getQuantity(id) {
return document.getElementById(id).getAttribute("quantity");
}
var quantId1 = getQuantity('id1');
var quantId2 = getQuantity('id2');
getElement*s*ById is returning an array. You need to get the individual items. If you had a whole lot of elements you could select by class product and write a simple function to loop over them and create an array.
There is no such function as getElementsById. you can use either getElementsByClassName or getElementsByName
https://developer.mozilla.org/en/DOM/document.getElementsByName
https://developer.mozilla.org/en/DOM/document.getElementsByClassName
Take note that getElementsByClassName is fairly new and not supported by older browsers.
Using pure JavaScript you need to write your own function for that:
function getQuantities() {
var args = Array.prototype.slice.call(arguments);
var quantityValue = 0;
for(var i = 0; i < args.length; i++) {
quantityValue += document.getElementsById(args[i]).getAttribute("quantity");
}
return quantityValue;
}
// your code
productMinimum : getQuantities("id1","id2")
As I understand it, document.getElementById takes one id at the time
Also, consider using html5 custom data attributes
There is no such thing as getElementsById()
But there is querySelectorAll, but it's not supported in IE7 and older though
here's a sample which should return the two <li> in a nodelist.
document.querySelectorAll('#id1, #id2')
You can only get one element at once (with getElementById()). If you want an array containing the quantities, use this:
[document.getElementById('id1').getAttribute('quantity'), document.getElementById('id2').getAttribute('quantity')]
Consider using jQuery, there you can use $('#id1, #id2') and besides that it supports easy access to data- attributes - what you are doing right now is invalid HTML since li does not have price or quantity attributes:
<li class="product" name="RD-101" id="id1" data-price="18" data-quantity="2">
<li class="product" name="RD-101" id="id2" data-price="18" data-quantity="4">
To get the quantities array:
$('#id1, #id2').map(function() { return $(this).data('quantity'); }).get();

How to polyfill RadioNodeList?

Background
As per the WHATWG someForm.elements should return a HTMLFormElementsCollection.
A HTMLFormElementsCollection returns a RadioNodeList if multiple elements share the same name.
The RadioNodeList has special value semantics where it returns the value of the first checked radio list in the nodelist.
This would allow the following answer to work if it were implemented
I naively attempted a polyfill that is based on host objects being well behaved (as per WebIDL), which they are clearly not.
Question
What is an alternative efficient implementation for this polyfill whilst we wait for browsers to become either RadioNodeList or WebIDL compliant?
Example Reference code
<form name="myform">
<input type="radio" name="foo" value="10" /> foo
<input type="radio" name="foo" value="30" /> bar
</form>
var myform = document.forms.myform;
var radio = myform.elements.foo;
var price = radio.value;
Naive attempt reference code
(function () {
var pd = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, "elements"),
getElements = pd.get;
pd.get = get;
Object.defineProperty(HTMLFormElement.prototype, "elements", pd);
function get() {
var elements = getElements.call(this);
if (elements.length) {
Object.defineProperty(elements, "value", {
get: getRadioNodeListValue,
set: setRadioNodeListValue,
configurable: true
});
}
return elements;
}
function getRadioNodeListValue() {
for (var i = 0, len = this.length; i < len; i++) {
var el = this[i];
if (el.checked) {
return el.value;
}
}
}
function setRadioNodeListValue(value) {
for (var i = 0, len = this.length; i < len; i++) {
var el = this[i];
if (el.checked) {
el.value = value;
return;
}
}
}
}());
If you can accept bolting the value getter onto NodeList then the following should work
RadioNodeList polyfill
Credit to ##Esailija
you could also just add .value to NodeList prototype
Why? You know that requiring ES5 and WebIDL will fail in common-use browsers on the web today. Your code requires new features, both in ES5 )Object.getOwnPropertyDescriptor, get/set), and as you mentioned WebIDL interface objects being well-behaved.
The HTML5 polyfill aims at generalization and generally fails at implementation. It is a harmful trend.
Don't modify other objects. Especially host objects.
If there is any variance from the HTML5 definition (and there is) there will be problems when a second feature-testing script tries to detect if there is a value property on a group of radios, and then assumes standard support.It's way off from the 2-step algorithm in HTML5.
Your code polyfills only if elements.length > 0 and affects checkboxes, too (keep in mind that radios aren't the only element with a checked property). Your setter changes the value of the first checked radio. Shouldn't setting the value check the first unchecked radio of that name, having that value?
Instead, write functions that are only as general as you need them to be.
function getRadioValue(form, radioName) {
// Delete comment, write code.
}
function setRadioValue(form, radioName, value) {
// Delete comment, write code.
}

Getting element by a custom attribute using JavaScript

I have an XHTML page where each HTML element has a unique custom attribute, like this:
<div class="logo" tokenid="14"></div>
I need a way to find this element by ID, similar to document.getElementById(), but instead of using a general ID, I want to search for the element using my custom "tokenid" attribute. Something like this:
document.getElementByTokenId('14');
Is that possible? If yes - any hint would be greatly appreciated.
Thanks.
It is not good to use custom attributes in the HTML. If any, you should use HTML5's data attributes.
Nevertheless you can write your own function that traverses the tree, but that will be quite slow compared to getElementById because you cannot make use of any index:
function getElementByAttribute(attr, value, root) {
root = root || document.body;
if(root.hasAttribute(attr) && root.getAttribute(attr) == value) {
return root;
}
var children = root.children,
element;
for(var i = children.length; i--; ) {
element = getElementByAttribute(attr, value, children[i]);
if(element) {
return element;
}
}
return null;
}
In the worst case, this will traverse the whole tree. Think about how to change your concept so that you can make use browser functions as much as possible.
In newer browsers you use of the querySelector method, where it would just be:
var element = document.querySelector('[tokenid="14"]');
This will be much faster too.
Update: Please note #Andy E's comment below. It might be that you run into problems with IE (as always ;)). If you do a lot of element retrieval of this kind, you really should consider using a JavaScript library such as jQuery, as the others mentioned. It hides all these browser differences.
<div data-automation="something">
</div>
document.querySelector("div[data-automation]")
=> finds the div
document.querySelector("div[data-automation='something']")
=> finds the div with a value
If you're using jQuery, you can use some of their selector magic to do something like this:
$('div[tokenid=14]')
as your selector.
You can accomplish this with JQuery:
$('[tokenid=14]')
Here's a fiddle for an example.
If you're willing to use JQuery, then:
var myElement = $('div[tokenid="14"]').get();
Doing this with vanilla JavaScript will do the trick:
const something = document.querySelectorAll('[data-something]')
Use this more stable Function:
function getElementsByAttribute(attr, value) {
var match = [];
/* Get the droids we are looking for*/
var elements = document.getElementsByTagName("*");
/* Loop through all elements */
for (var ii = 0, ln = elements.length; ii < ln; ii++) {
if (elements[ii].nodeType === 1){
if (elements[ii].name != null){
/* If a value was passed, make sure it matches the elements */
if (value) {
if (elements[ii].getAttribute(attr) === value)
match.push(elements[ii]);
} else {
/* Else, simply push it */
match.push(elements[ii]);
}
}
}
}
return match;
};

Categories

Resources