HTML - List within a list - Indexing a button press - javascript

I am trying to identify a button press in JavaScript that is coming from a list within a list.
In basic terms I am thinking of it like a 2x2 table - e.g. if the top-right button is pressed it's index should be 0-1. If bottom-right is pressed it's index should be 1-1. If top-left is pressed index should be 0-0, bottom left 1-0 etc.
The child lists make up the parent list's rows, and are comprised of a series of pre-defined templates. Each child list's ID is generated using the index id of the parent list.
I'd like to know best practice for this. I am using Crestron's ch5 library but am hoping I can apply generic principles to my problem.
I have simplified the HTML:
<template id="template-0">
<button id="post-1">Item 1</button>
</template>
<template id="template-1">
<button id="post-2">Item 2</button>
</template>
<ch5-list
class="verticalList"
size="2"
indexid="idy"
id="parent-list"
orientation="vertical">
<template>
<ch5-list
class="horizontalList"
id="child-list{{idy}}"
size="2"
indexid="idx"
orientation="horizontal">
<template>
<ch5-template templateid="template-{{idx}}">
</ch5-template>
</template>
</ch5-list>
</template>
</ch5-list>
I am new to HTML so apologies if this is a stupid question.
Any help would be much appreciated!
Thank you

Accessing elements in your HTML can be done with several DOM methods: getElementById, getElementsByTagName, getElementsByClassName, and querySelectorAll.
NOTE: In all of the methods which return a collection of resulting elements, the elements will be sorted in the order which they appear on the page (in the HTML), so the button order would be as follows:
let order = [
"top-left button", "top-right button",
"bottom-left button", "bottom-right button"
];
getElementById
If the resulting button elements from the template are placed, they'll just have id values of post-1 for template-0 and post-2 for template-1. With two columns and two rows of buttons, this means that there will only be two unique IDs between four buttons, so selecting elements this way may not work well. The HTML code could probably be modified to be sure that the buttons have unique ID attributes. If that problem can be solved, the following code will select one button at a time:
let buttonElement = document.getElementById("post-1");
// or if ID can be resolved to be post-0-1:
let firstButtonElement = document.getElementById("post-0-1");
getElementsByTagName
Using this method would work fine as it will return a list of button elements as an HTMLCollection. It may return other buttons on the page, but it's a minor inconvenience that can be handled.
let buttonsCollection = document.getElementsByTagName("button");
getElementsByClassName
If you use a class for the buttons instead of IDs, the buttons can all be assigned to the same class and this is one of the ways it will select all of the buttons as intended and none of the buttons you didn't intend.
<template id="template-0">
<button id="post-1" class="postButtons">Item 1</button>
</template>
<template id="template-1">
<button id="post-2" class="postButtons">Item 2</button>
</template>
let postButtons = document.getElementsByClassName("postButtons");
console.log(postButtons);
querySelectorAll
This method combines multiple selection types into one (using CSS selectors):
Description
Method call
Tag names
document.querySelectorAll("button");
Class names
document.querySelectorAll(".postButtons");
Id
document.querySelectorAll("#post-1");
Since you can use CSS Selectors, you could select buttons with id values that start with post- like so:
let postButtons = document.querySelectorAll("button[id^=post-]");
I'm not entirely sure how the elements will be created as for the CH5 library, but these steps provided above should help you get started.
Code Snippet showing all of the above examples in one:
let buttonElementPostOne = document.getElementById('post-1');
console.log("getElementById:".padStart(23, " "), buttonElementPostOne);
let buttonsCollection = document.getElementsByTagName('button');
console.log("getElementsByTagName:".padStart(23, " "), buttonsCollection[0]);
let postButtons = document.getElementsByClassName('postButtons');
console.log("getElementsByClassName:".padStart(23, " "), postButtons[0]);
let postButtonsQueried = document.querySelectorAll('button[id^=post-');
console.log("querySelectorAll:".padStart(23, " "), postButtonsQueried[0]);
<div>
<button id="post-1" class="postButtons">post-1</button>
<button id="post-2" class="postButtons">post-2</button>
</div>
<div>
<button id="post-3" class="postButtons">post-3</button>
<button id="post-4" class="postButtons">post-4</button>
</div>

Related

javascript not working when using 2 element with same id, one is hidden another is shown [duplicate]

Can multiple HTML elements have the same ID if they're of different element types? Is a scenario like this valid? Eg:
div#foo
span#foo
a#foo
No.
Element IDs should be unique within the entire document.
I think there is a difference between whether something SHOULD be unique or MUST be unique (i.e. enforced by web browsers).
Should IDs be unique? YES.
Must IDs be unique? NO, at least IE and FireFox allow multiple elements to have the same ID.
Can multiple elements have the same ID?
Yes - whether they are the same tag or not, browsers will render the page even if multiple elements have the same ID.
Is it Valid HTML?
No. This is still true as of the HTML 5.1 spec. However, the spec also says getElementById must return the first element with the given ID, making the behavior not undefined in the case of an invalid document.
What are the consequences of this type of invalid HTML?
Most (if not all) browsers select the first element with a given ID, when calling getElementById. Some libraries that find elements by ID inherit this behavior, while newer libraries (as gman points out in his answer) will use the more explicit querySelector and querySelectorAll methods, which unambiguously select either the first or all matching elements, respectively. Most (if not all) browsers also apply styles assigned by id-selectors (e.g. #myid) to all elements with the specified ID. If this is what you expect and intend, then there are no unintended consequences. If you expect/intend something else (e.g. for all elements with that ID to be returned by getElementById, or for the style to apply to only one element) then your expectations will not be met and any feature relying on those expectations will fail.
Some javascript libraries do have expectations that are not met when multiple elements have the same ID (see wootscootinboogie's comment about d3.js)
Conclusion
It's best to stick to the standards, but if you know your code works as expected in your current environments, and these IDs are used in a predictable/maintainable way, then there are only 2 practical reasons not to do this:
To avoid the chance that you are wrong, and one of the libraries you use actually does malfunction when multiple elements have the same ID.
To maintain forward-compatibility of your website/application with libraries or services (or developers!) you may encounter in the future, that do malfunction when multiple elements have the same ID - which is a reasonable possibility since this is not, technically, valid HTML.
The power is yours!
Even if the elements are of different types it can cause you some serious problems...
Suppose you have 3 buttons with the same id:
<button id="myid" data-mydata="this is button 1">button 1</button>
<button id="myid" data-mydata="this is button 2">button 2</button>
<button id="myid" data-mydata="this is button 3">button 3</button>
Now you setup some jQuery code to do something when myid buttons are clicked:
$(document).ready(function ()
{
$("#myid").click(function ()
{
var buttonData = $(this).data("mydata");
// Call interesting function...
interestingFunction();
$('form').trigger('submit');
});
});
What would you expect? That every button clicked would execute the click event handler setup with jQuery. Unfortunately it won't happen. ONLY the 1st button calls the click handler. The other 2 when clicked do nothing. It is as if they weren't buttons at all!
So always assign different IDs to HTML elements. This will get you covered against strange things. :)
<button id="button1" class="mybtn" data-mydata="this is button 1">button 1</button>
<button id="button2" class="mybtn" data-mydata="this is button 2">button 2</button>
<button id="button3" class="mybtn" data-mydata="this is button 3">button 3</button>
Now if you want the click event handler to run when any of the buttons get clicked it will work perfectly if you change the selector in the jQuery code to use the CSS class applied to them like this:
$(document).ready(function ()
{
$(".mybtn").click(function ()
{
var buttonData = $(this).data("mydata");
// Call interesting function...
interestingFunction();
$('form').trigger('submit');
});
});
No. two elements with the same id are not valid. IDs are unique, if you wish to do something like that, use a class. Don't forget that elements can have multiple classes by using a space as a delimeter:
<div class="myclass sexy"></div>
How about a pragmatic answer.
Let's go to youtube and run this code
Object.fromEntries(Object.entries([...document.querySelectorAll('[id]')].reduce((s, e) => { s[e.id] = (s[e.id] || 0) + 1; return s; }, {})).filter(([k,v]) => v > 1))
and see all the repeated ids.
Changing the code above to show ids repeated more than 10 times here's the list it produced
additional-metadata-line: 43
avatar: 46
avatar-link: 43
button: 120
buttons: 45
byline-container: 45
channel-name: 44
container: 51
content: 49
details: 43
dismissable: 46
dismissed: 46
dismissed-content: 43
hover-overlays: 45
img: 90
menu: 50
meta: 44
metadata: 44
metadata-line: 43
mouseover-overlay: 45
overlays: 45
repeat: 36
separator: 43
text: 49
text-container: 44
thumbnail: 46
tooltip: 80
top-level-buttons: 45
video-title: 43
video-title-link: 43
Other sites that use the same id more than once include Amazon.com, ebay.com, expedia.com, cnn.com
clearly ids are just another piece of metadata on an element.
getElementById is pretty much obsolete. You can use querySelectorAll for all elements or querySelector for the first, regardless of selector so if you want all elements with id foo then
document.querySelectorAll('#foo') // returns all elements with id="foo"
where as if you want only the first element use querySelector
document.querySelector('#foo') // returns the first element with id="foo"
document.querySelector('.foo') // returns the first element with class "foo"
document.querySelector('foo') // returns the first <foo> element
document.querySelector('foo .foo #foo') // returns the first element with
// id="foo" that has an ancestor
// with class "foo" who has an
// ancestor <foo> element.
And we can see that using selectors we can find different elements with the same id.
function addClick(selector, add) {
document.querySelector(selector).addEventListener('click', function() {
const e = this.parentElement.querySelector('#value');
e.textContent = parseInt(e.textContent) + add;
});
}
addClick('.e #foo', 1);
addClick('.f #foo', 10);
body { font-size: x-large; font-weight: bold; }
.a #foo { color: red; }
.b #foo { color: green; }
div:nth-child(3) #foo { color: blue; }
#foo { color: purple }
<div class="a"><span id="foo">a</span></div>
<div class="b"><span id="foo">b</span></div>
<div><span id="foo">c</span></div>
<span id="foo">d</span>
<div class="e"><button type="button" id="foo">+1</button>: <span id="value">0</span></div>
<div class="f"><button type="button" id="foo">+10</button>: <span id="value">0</span></div>
Where it matters that id is unique
<a> tags can reference ids as in <a href="#foo">. Clicking it will jump the document to the first element with id="foo". Similarly the hash tag in the URL which is effectively the same feature.
<label> tags have a for attribute that specifies which element they are labeling by id. Clicking the label clicks/activates/give-the-focus-to the corresponding element. The label will only affect the first element with a matching id
label { user-select: none; }
<p>nested for checking</p>
<form>
<div><input type="checkbox" id="foo"><label for="foo">foo</label></div>
</form>
<form>
<div><input type="checkbox" id="foo"><label for="foo">foo (clicking here will check first checkbox)</label></div>
</form>
Otherwise, id is just another tool in your toolbox.
The official spec for HTML states that id tags must be unique AND the official spec also states that if the render can be completed, it must (i.e. there are no such thing as "errors" in HTML, only "invalid" HTML). So, the following is how id tags actually work in practice. They are all invalid, but still work:
This:
<div id="unique">One</div>
<div id="unique">Two</div>
Renders fine in all browsers. However, document.getElementById only returns an object, not an array; you will only ever be able to select the first div via an id tag. If you were to change the id of the first div using JavaScript, the second ID would then be accessible with document.getElementById (tested on Chrome, FireFox & IE11). You can still select the div using other selection methods, and it's id property will be returned correctly.
Please note this above issue opens a potential security vulnerability in sites that render SVG images, as SVGs are allowed to contain DOM elements, and also id tags on them (allows script DOM redirects via uploaded images). As long as the SVG is positioned in the DOM before the element it replaces, the image will receive all JavaScript events meant for the other element.
This issue is currently not on anyone's radar as far as I know, yet it's real.
This:
<div id="unique" id="unique-also">One</div>
Also renders fine in all browsers. However, only the first id you define this way is utilized, if you tried document.getElementById('unique-also'); in the above example, you would be returned null (tested on Chrome, FireFox & IE11).
This:
<div id="unique unique-two">Two</div>
Also renders fine in all browsers, however, unlike class tags that can be separated by a space, the id tag allows spaces, so the id of the above element is actually "unique unique-two", and asking the dom for "unique" or "unique-two" in isolation returns null unless otherwise defined elsewhere in the DOM (tested on Chrome, FireFox & IE11).
SLaks answer is correct, but as an addendum note that the x/html specs specify that all ids must be unique within a (single) html document. Although it's not exactly what the op asked, there could be valid instances where the same id is attached to different entities across multiple pages.
Example:
(served to modern browsers) article#main-content {styled one way}
(served to legacy) div#main-content {styled another way}
Probably an antipattern though. Just leaving here as a devil's advocate point.
And for what it's worth, on Chrome 26.0.1410.65, Firefox 19.0.2, and Safari 6.0.3 at least, if you have multiple elements with the same ID, jquery selectors (at least) will return the first element with that ID.
e.g.
<div id="one">first text for one</div>
<div id="one">second text for one</div>
and
alert($('#one').size());
See http://jsfiddle.net/RuysX/ for a test.
Well, using the HTML validator at w3.org, specific to HTML5, IDs must be unique
Consider the following...
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MyTitle</title>
</head>
<body>
<div id="x">Barry</div>
<div id="x">was</div>
<div id="x">here</div>
</body>
</html>
the validator responds with ...
Line 9, Column 14: Duplicate ID x. <div id="x">was</div>
Warning Line 8, Column 14: The first occurrence of ID x was here. <div id="x">Barry</div>
Error Line 10, Column 14: Duplicate ID x. <div id="x">here</div>
Warning Line 8, Column 14: The first occurrence of ID x was here. <div id="x">Barry</div>
... but the OP specifically stated - what about different element types. So consider the following HTML...
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>MyTitle</title>
</head>
<body>
<div id="x">barry
<span id="x">was here</span>
</div>
</body>
</html>
... the result from the validator is...
Line 9, Column 16: Duplicate ID x. <span id="x">was here</span>
Warning Line 8, Column 14: The first occurrence of ID x was here. <div id="x">barry
Conclusion:
In either case (same element type, or different element type), if the id is used more than once it is not considered valid HTML5.
Yes they can.
I don't know if all these anwers are outdated, but just open youtube and inspect the html. Try to inspect the suggested videos, you'll see that they all have the same Id and repeating structure as follows:
<span id="video-title" class="style-scope ytd-compact-radio-renderer" title="Mix - LARA TACTICAL">
<div id="one">first text for one</div>
<div id="one">second text for one</div>
var ids = document.getElementById('one');
ids contain only first div element. So even if there are multiple elements with the same id, the document object will return only first match.
It's possible to have duplicate ids. I have tried adding the todoitem from javascript and it added to the dom successfully. This is the html code and javscript code.
Nope, IDs have to be unique. You can use classes for that purpose
<div class="a" /><div class="a b" /><span class="a" />
div.a {font: ...;}
/* or just: */
.a {prop: value;}
Is it possible to have more than one student in a class having same Roll/Id no? In HTMLid attribute is like so. You may use same class for them. e.g:
<div class="a b c"></div>
<div class="a b c d"></div>
And so on.
We can use class name instead of using id. html id are should be unique but classes are not. when retrieving data using class name can reduce number of code lines in your js files.
$(document).ready(function ()
{
$(".class_name").click(function ()
{
//code
});
});
I think you can't do it because Id is unique you have to use it for one element . You can use class for the purpose

Get child of node by class (HTML template)

I have a template that I will be adding to the body multiple times. Each one will be the same, except the text in one of the elements will be different:
<template id="template">
<div class="activity">
<p class="activityName"> <!-- to be changed -->
</p>
<button type="button" class="btn btn-primary">
Edit
</button>
</div>
</template>
When I insert this template, I wants to edit the .activityName element. I could use Node.firstChild, but that means that if I change the HTML in the future, this setup might break if that element is no longer the first child. Is it possible to retrieve it by class from that specific node? Something like this:
var template = document.getElementById("template");
var clon = template.content.cloneNode(true);
clon.getElementsByClassName("activityName")[0].innerHTML = "text"; //this line needs changing
document.body.append(clon);
what about using:
clon.querySelector(".activityName").innerHTML = "text";
The querySelector function finds the first element that matches with the specified selector, in this case, our selector is .activityName this means that the function will find the first element with the class activityName
Now, if you want to get all the nodes with the same class, you have to use the querySelectorAll function
I see that you used template.content but there is not a property called content in template element, maybe you mean something like var clon = template.querySelector('.activity')

Finding the index of the first array element that contains a particular CSS class using native javascript

I have a few buttons on a page who all share the .btn class, and only one at any particular time has another class, .selected, which changes.
In my javascript I've declared an array of elements with the .btn class:
var btn = document.querySelectorAll(".btn");
I'd like to get the index of the first (and in this case, only) occurrence of the element that also has the .selected class.
So far as I can tell, without using jQ, I should be able to find the index by using findIndex(), which requires a testing function. I can't seem to write that function though, because I am unsure how to test if an element from an array contains a particular class, without having a variable for the individual element.
Finding the index of the element with class in native Javascript -- This question from Nov '16 seems to ask nearly the same question but the top answer suggests using document.getElementByClass() which returns an array, not an index.
Test if an element contains a class? -- Here the answer appears to work for testing individual elements, but if I'm trying to iterate through an array without a loop (i.e. using something like findIndex()), I don't have a var to use the element.contains notation.
So more or less I'm trying to pass the index into another function:
someFunc(btn.findIndex(element.classList.contains("selected"));, but struggling to determine how to use the indexOf() method to find the location.
I'm obviously new to JS, so any and all help is appreciated!
Use Document#querySelector to get a single reference, and use as you're query .btn.selected, and if there are any other elements with .selected, or just .selected if it can be attached only to .btn:
console.log(document.querySelector(".btn.selected"));
console.log(document.querySelector(".selected"));
/** you can use the reference to change the button styling **/
document.querySelector(".selected").style.background = "green";
/** you can assign it to a variable to use multiple times **/
var selected = document.querySelector(".selected");
selected.style.border = "1px solid red";
selected.style.color = "white";
<button class="btn">1</button>
<button class="btn">2</button>
<button class="btn selected">3</button>
<button class="btn">4</button>
<button class="btn">5</button>
If we really do want the index, use Document#querySelectorAll to get a list of btns, and use Array#findIndex to find the element that is equal to the one you got using Document#querySelector.
Since the list of nodes is not a true array, we need to convert it to an array (using Array#from for example), or use Function#call to invoke findIndex on it.
var btns = document.querySelectorAll('.btn');
var selected = document.querySelector(".selected");
var index = [].findIndex.call(btns, function(btn) {
return btn === selected;
});
console.log(index);
<button class="btn">1</button>
<button class="btn">2</button>
<button class="btn selected">3</button>
<button class="btn">4</button>
<button class="btn">5</button>

Need help traversing elements with jQuery in ASP.NET Web Forms

So the first image is what my page looks like. I am trying to grab the ID of the table at the top (the table that has the class trackingHistory and ID of ContentPlaceHolder1_ctl00_myRpt_ctl00_0_gdvTH_0) whenever the highlighted button is clicked.
Right now I can click the button and fire my Javascript method 'ToggleHistory()' and use jQuery to get the ID of my button. (.attr('id'))
but that's about as far as i can get. I have tried messing around with the closest() and prev() methods from jQuery but haven't had any luck. any help would be appreciated.
javascript method
function ToggleHistory(button)
{
console.log(button);
var x = $(button).prev();
var y = $(button).closest('table').find('.trackingHistory');
//var z = $(button).closest('.trackingHistory');
console.log(x);
console.log(y);
console.log($(y).attr('id'));
//console.log(z);
}
i dont want to have to hard code the id because there will be a dynamic amount of these tables and buttons.
The <button> and the <table> don't have a direct relationship, but do have the <div> in common.
<div>
<table class="toggleHistory"></table>
</div>
<button type="button" onclick="ToggleHistory(this)">click here to hide</button>
The <button> and <div> are immediate siblings, which you can traverse between with .prev() and back with .next().
$(button).prev()...
Then, the <div> and <table> are .parent() and child (.children()):
$(button).prev().children('.trackingHistory');
function ToggleHistory(button)
{
console.log(button);
var $historyTable = $(button).prev().children('.trackingHistory');
console.log($historyTable.attr('id'));
}

When using getElementsByTagName, how to I specify which element is clicked

I am setting up a lightweight website for my work. It has very little JS interactivity, using only GSAP with TweenLite() for 1-2 animations. Since its nearly all unique elements, I can mostly use getElementById() to cover them.
One problem involving multiple similar elements is causing me grief. An accordion style menu:
<button>MenuOne</button>
<div>
<a>...</a>
<a>...</a>
<a>...</a>
</div>
<button>MenuTwo</button>
<div>
<a>...</a>
<a>...</a>
<a>...</a>
</div>
<button>MenuThree</button>
<div>
<a>...</a>
<a>...</a>
<a>...</a>
</div>
The idea is on the click of the Menu button, the next div becomes visible, and any over divs become invisible (all hidden by default).
If I were to use jQuery, this could be achieved by
$('button').click(function() {
//show/hide stuff
});
I don't want to use jQuery for a single selector, when GSAP covers all the animations I've used so far:
var btn = document.getElementsByTagName('button');
btn.onmouseup=function () {
//show/hide stuff
};
However, this doesn't seem to react to me clicking the buttons (no errors, nothing). I have the same function() inside, and can get it to work by putting the btn variable into jquery $(btn).click(.
I can only assume the btn variable is watching all buttons, and cannot distinguish which one (if any) I've just clicked. Something that jQuery library is able to do.
Hunting around online, I've found that the native JS result is slightly different to the jquery one. In the console, jquery shows up as an Object[array], and javascript shows as an HTMLCollectionT[array]. My guess is part of the jquery array has some marker that isn't there by default.
I found how to pick [0] [1] [2] buttons in the array and can add unique IDs, but am sure I can solve this without 3 separate functions.
You will get the list of buttons by document.getElementsByTagName() function, if you want to attach particular function at click of particular button then you can do like this.
var allButtons = document.getElementsByTagName('button');
for(var i=0; i<allButtons.length; i++){
allButtons[i].onclick = myfunction;
}
function myfunction (){
alert(this.id);
}
Here is the demo with http://jsfiddle.net/M697C/
The jQuery binding will automatically loop through all of the captured elements, and manually bind to each one. When you are using vanilla JS, you have to do the looping yourself:
var btn = document.getElementsByTagName('button');
for (var i = 0; i < btn.length; i++) {
btn[i].onmouseup = function () {
// show/hide code here.
});
}

Categories