Javascript Getting specific element (of parent) by name - javascript

I'm using custom tags to define sections in an application, so I have something like this:
<mysection>
<form>
<input name="myfield">
</form>
</mysection>
I'm using the following and able to get the tag (printed to console, everything is groovy)
var parent = document.getElementsByTagName('mysection');
The issue I'm having is finding the child field by name:
var myfield = parent.getElementsByName("myfield");
...as I don't want to pick up on any other 'sections' that might have an input with the name 'myfield'.
EDIT:
var parent = document.getElementsByTagName('mysection')[0];
was suggested and returns to console the section contents, however, getElementsByName throws an error:
Uncaught TypeError: Object #<NodeList> has no method 'getElementsByName'

Using getElementsByTagName() and getElementsByName() will return a NodeList, you need to get the first element of the list like this:
var parent = document.getElementsByTagName('mysection')[0];
var myfield = parent.getElementsByName("myfield")[0];
Edit
You were correct, getElementsByName is not valid for an element. I am unsure how to localize the functionality of it as you are trying to do. It seems that it will only work for document. You may have to write your own implementation of getElementsByName if you want to use it in a localized scope.
Second Edit
To be nice, I made that implementation for you :D Here it is in all its "glory".
Element.prototype.getElementsByName = function (arg) {
var returnList = [];
(function BuildReturn(startPoint) {
for (var child in startPoint) {
if (startPoint[child].nodeType != 1) continue; //not an element
if (startPoint[child].getAttribute("name") == arg) returnList.push(startPoint[child]);
if (startPoint[child].childNodes.length > 0) {
BuildReturn(startPoint[child].childNodes);
}
}
})(this.childNodes);
return returnList;
};
var parent = document.getElementsByTagName('mysection')[0];
var myfield = parent.getElementsByName("myfield")[0];
Small fix
I was incorrectly passing the element and not its children into the recursion. The code above has been edited with the proper argument passed now. See working fiddle: http://jsfiddle.net/js6NP/5/

I actually found a much more simple way to handle this:
document.querySelectorAll('mysection [name="myfield"]');
Here you can see an example where it only modifies the field inside the section specified: http://jsfiddle.net/fluidbyte/kph6H/
qSA supports modern browsers and is compatible down to IE8, Here's a polyfill to support back to IE7: https://gist.github.com/2724353

getElementsByName won't work on a DOM element reference. Use querySelector or querySelectorAll instead. In example:
var parent = document.getElementsByTagName('mysection')[0];
var myfield = parent.querySelector("[name='myfield']");

Just use an ID instead:
<mysection>
<form>
<input name="myfield" id="fieldName">
</form>
</mysection>
var myfield = document.getElementById("fieldName");
ID's are supposed to be unique on a page. So you shouldn't have trouble accessing the right element.
If you really have to use name/tagname, getElementsByTagName and getElementsByName both always return a array (A empty one if no element was found). you can access the right element, just like you'd access elements in arrays:
document.getElementsByTagName('mysection')[0]; For the first element with tagname mysection.

Related

Adding active class based on current URL

I am trying to add 'active' class on my link. It is saying
Uncaught TypeError: document.querySelectorAll(...).each is not a function
Here is my code:
const current = window.location.href;
document.querySelectorAll("#nav-tab a").each(function(){
const $this = this;
if($this.attr('href').indexOf(current) !== -1){
$this.classList.add("active");
}
});
Can you help me? Or is there a better way to add a class name based on the current URL?
Thank you so much!
You have a few issues with your code. Your mainly confusing jQuery methods with regular native browser methods/conventions:
You need to use .forEach() and not .each(). The .forEach() method is a method on the NodeList that querySelectorAll() returns.
.attr() is not a valid method. To get an element's attribute you can use .getAttribute(). We can use .href here instead to get the href. Note that getAttribute("href") will retrieve the URL as it is in your mark-up, whereas .href will retrieve the full, eg, if you had href="/foo/bar", .href will give https://example.com/foo/bar, whereas .getAttribute() will return just /foo/bar.
Use the element parameter of the function instead of this. When you use .forEach() you're iterating over the elements in your NodeList (ie: the elements you selected), so you can access each using the first parameter of the forEach callback. The this value in the browser (if not in strict mode) will default to window, so it won't be the element like you're expecting it to be:
const current = window.location.href;
document.querySelectorAll("#nav-tab a").forEach(function(elem){
if(elem.href.includes(current)){
elem.classList.add("active");
}
});
I've also changed .indexOf(...) !== -1 to .includes(), which is a more modern way to check if a string contains another value.
I will point out that you can make your query selector more advanced, which will limit the number of elements you iterate:
const current = window.location.href;
document.querySelectorAll(`#nav-tab a[href*="${current}"]`).forEach(elem => {
elem.classList.add("active");
});
This uses the attribute selector a[href*=...] to select the a elements that have a href that contains the text in stored in current.
I think that you are mistaken jQuery and vanilla javascript
$.each is a jQuery function in you case you can use .forEach
$.attr is a jQuery function in you case you can use .getAttribute
const current = "#test-3";//window.location.href;
document.querySelectorAll("#nav-tab a").forEach((elem) => {
if (elem.getAttribute('href').indexOf(current) !== -1) {
elem.classList.add("active");
}
});
.active { color:red}
<div id="nav-tab">
Test 1
Test 2
Test 3
Test 4
</div>

difference in jQuery object context

I'm trying to obtain some insight in the context of a jQuery object. I've read a ton of questions on SO, but they all want to fix a specific problem, and the answers thus fix the problem, and didn't really provide me with the knowledge I'm hoping for.
So I have two buttons, and I have a click event that listens for both of them like so:
$('#copyRI, #copyIR').click(function () {...}
Now when I try to establish which element was clicked like so:
$('#copyRI, #copyIR').click(function () {
var test = $(this);
var test2 = $('#copyIR');
var compare = (test === test2);
}
compare returns false when I click the button with id = #copyIR
when debugging I noticed that the context of test and test2 are different:
Now I'm not looking for a way to successfully establish which button was clicked, but I want to obtain some insight in the concept of the "context" in a jQuery object.
Thanks in advance!
When you call $(this) you create a new jQuery object, instantiating it with an HTML Element.
When you call $('#copyIR') you create a new jQuery object, instantiating it with a selector. This stores extra information in the object, including the selector itself.
Even if that wasn't the case, you would be creating two different objects and === (and == for that matter) test if two objects are the same object not if they are identical objects.
$(this) === $(this) would also return false.
If you want to test if the elements are the same, then you can compare the underlying DOM nodes (since those will be the same object)
var compare = (test[0] === test2[0]);
Alternatively, you can just test if the object you have in the first place matches the selector:
var compare = test.is('#copyIR');
You should rather use .is() method here.
Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
CODE:
$('#copyRI, #copyIR').click(function () {
var test = $(this);
var compare = test.is('#copyIR')
}
jQuery contexts (being an Object) are compared by reference, so test === test2 would obviously return false since each variable is pointing to a different jQuery context (the fact that both contexts internally contains a reference to same DOM object doesn't matter).
Try is() instead:
var compare = $(this).is('#copyIR');
See Documetnation
You could simply compare the id.
$('#copyRI, #copyIR').click(function () {
if (this.id === 'copyIR') {
// do something
}
}

Why can't I make "onclick" event handler in my javascript code? [duplicate]

I am using getElementById when I need to clone the div element.
Code:
printHTML( document.getElementById("div_name").cloneNode(true));
Now I need to use getElementsByClassName
CloneNode is not working when using getElementsByClassName.
How can I put class name in here?
Thank's
EDIT:
When I try to use this:
printHTML( $('.dataTables_scroll').clone(true) );
You can see my function:
function printHTML(clonedDive){
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.contentWindow.onunload = function(){
$(".DTTT_container").show("fast");
$("#header_outer").show("fast");
$(".ColVis.TableTools").show("fast");
$("#footer").show("fast");
};
iframe.contentWindow.document.body.appendChild(clonedDive);
iframe.contentWindow.print();
document.body.removeChild(iframe);
}
I am getting an error in this line:
iframe.contentWindow.document.body.appendChild(clonedDive);
This is an error description:
Uncaught Error: NOT_FOUND_ERR: DOM Exception 8
getElementsByClassName gets a nodelist, or an array-like object containing elements if you will, as there can be more than one element with the same class.
getElementsByClassName does this even if only one element matches the class.
You can generally recognize methods like that be the s in getElements, which means it gets multiple elements, i.e. a nodeList.
getElementById only gets one element as ID's are unique.
To get the first element in the nodelist, use bracket notation, like so:
document.getElementsByClassName("div_name")[0].cloneNode(true);
or one could use querySelector, which gets the first matching element only
document.querySelector(".div_name").cloneNode(true);
The jQuery solution would be:
$('.div_name').clone(true);
and to iterate over elements with a certain classname you'd use a loop
var elems = document.getElementsByClassName("div_name");
for ( var i=0; i<elems.length; i++ ) {
printHTML( elems[i].cloneNode(true) );
}
Due to getElementsByClassName returns an object's array, so you have to use for loop to iterates among them, as follows:
for (i = 0; i < document.getElementsByClassName("div_name").length; i++){
printHTML( document.getElementsByClassName("div_name")[i].cloneNode(true));
}
otherwise, if you know the index of the element you have let we say 1
printHTML( document.getElementsByClassName("div_name")[1].cloneNode(true));
This does not work? :
printHTML( document.getElementsByClassName("class_name")[0].cloneNode(true));
You can loop through the elements and clone one by one...
var e = document.getElementsByClassName('div');
for (var i = 0; i < e.length; i += 1) {
// Clone e[i] here
console.log(e[i].cloneNode(true));
}

Prototype's weird supported environment check

I was looking at the PrototypeJS code and found this check -
var div = document.createElement('div'),
form = document.createElement('form'),
isSupported = false;
if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
isSupported = true;
}
I would like to know what are the cases where this condition returns false -
div['__proto__'] !== form['__proto__']
Note: The function's comment says - "Used internally to detect if the browser supports extending html element prototypes". Not sure how this check helps for that.
This test allows PrototypeJS to figure out if it can add specific methods to specific types of element prototypes.
For example you would not want the method getInputs() (which returns an array of all of the form's elements) on a <div> element because it would only make sense being used on a <form> element.
HTML
<div id='mydiv'></div>
<form id='myform'></form>
JS
$('mydiv').addClassName('newclass'); //does not error
$('myform').addClassName('newclass'); //does not error
$('myform').getInputs(); //does not error
$('mydiv').getInputs(); //throws error 'has no method getInputs()'
Example on JSFiddle http://jsfiddle.net/C39gu/

JavaScript setAttribute alternative

I am creating this JS function that creates an element
The function takes two parameters strName and objAttributes
function createElement( strName, objAttributes )
{
var elem = document.createElement(strName);
for ( var i in objAttributes )
elem.setAttribute(i, objAttributes[i]);
return elem;
}
This works fine in Fx, but not in MSIE
I know that the setAttibute method is buggy and the proposed workaround is
elem.attr = 'val';
But right now I have no idea how to write this inside my loop.
I have tried both elem.style and elem['style'] but none of them works.
Can anyone give me some advice,
thanks in advance
t
Use elem[i].
function createElement( strName, objAttributes )
{
var elem = document.createElement(strName);
for ( var i in objAttributes )
elem[i] = objAttributes[i];
return elem;
}
You can't just swap setting properties and setAttribute.
You have to be careful with setting properties on an element in place of using setAttribute.
Style properties and event handlers need to be carefully written, and those attributes that used to be minimized in html (disabled, multiple, readonly) have browser specific valid values.
Also, if you set element.class="mynewclass", you'll get an error, because class is a reserved javascript word, though it is perfectly safe to use it as a string in a setAttribute assignment. THe property name is '.className', and the proper name for a label's 'for' attribute is 'htmlFor'.
Let jQuery handle the cross-browser nonsense...
$(elem).attr(i, objAttributes[i]);

Categories