.onclick() only fires on the first button - javascript

When I clicked the "show comments" button, the result always only shown on the first button even though I clicked on the other button (2nd, 3rd, 4th, ... button). The content I wanted is shown correctly, but it doesn't displayed on the corresponding button.
I already tried to search the solutions over the internet, but I don't think I found the one, especially the other solutions answered with the use of jQuery. (No jQuery solutions, please)
HTML:
<div id="card-container" class="posts-card-container">
<p><strong><em>Press button "Show Posts" above (any user)</em></strong></p>
</div>
some of the script.js:
function showPosts(data) {
let cardContainer = document.getElementById('card-container').querySelector('p');
cardContainer.innerHTML = '';
for (let i = 0; i < data.length; i++) {
let cardTitle = '<h3>TITLE: '+data[i].title+'</h3>';
let cardBody = '<p><em>'+data[i].body+'</em><p>'
let btnShowComments = '<td><button id="button-show-comment" class="button-comments" postId='+data[i].id
+' onclick="loadComments('+data[i].id+')">Show Comments</button></td>';
let cardShowComments = '<div id="show-comments"><p></p></div>';
let newCard = '<div id="card-container" class="child-card-container">'+cardTitle
+cardBody+btnShowComments+cardShowComments+'</div>';
cardContainer.innerHTML += newCard;
}
}
function showComments(data) {
let commentContainer = document.getElementById("show-comments");
commentContainer.innerHTML = '<h2>Comments</h2>';
for (let i = 0; i < data.length; i++) {
let commentPoster = data[i].name+' '+'('+data[i].email+')';
let commentBody = data[i].body;
let newComment = '<p><strong>'+commentPoster+'</strong><em> commented:
"'+commentBody+'</em></p>';
commentContainer.innerHTML += newComment;
}
}
I expect the newComment is displayed on the corresponding button (e.g: if onclick() happened on the third button, newComment must be displayed under the third button), but in my code the newComment always displayed on the first button only.

By definition document.getElementById only returns one DOM element, the first one found, even if multiple with the same id are present. You should find a different way of selecting the button and you should also avoid having multiple elements with the same id as it's intended to be unique.
Edit: I realized after the fact that that's not exactly what you're asking, but you should still avoid using the same id multiple times, it causes all kinds of problems.

The problem here is that querySelector() in showPosts() will only ever give the first element in the set of elements you are looking through that fits the selector.
Which isn't what you want, so instead, we can hook up this function to an event, where we put an onclick event surrounding the div containing all buttons, and use event.target to select the button that you are using. So, something like:
document.getElementById("card-container").onclick=function showPosts(event)
{
let data=getData();//put in something like this
//no needed element checking since only buttons have onclick events
let cardContainer = event.target;
...
}
However, you will have to get data another way in order to use it in the function. Not to mention, you shouldn't be using the same ID in two differing elements. IDs are for directing the browser towards a single element.

Related

Appending elements to children of HTMLcollection

I am trying to loop through an HTMLcollection and append a newly created element to respective children. There are 3 conditions I am looking out for if the children have 1 of 3 classes:
type-freebie
type-hack
type-post
I have been able to set most of it up and successfully attach the element I need to each distinct group, however, when two or more of any distinct type appear, only one gets appended and the other one does not. I have tried to figure out what the problem and I think it has to do with something with HTMLcollections not working like arrays but I can't seem to be able to place my finger on it.
I have pasted the code snippets below of what I have so far and the HTML I am trying to manipulate in the DOM after it.
Would appreciate any insights I might be missing.
const card_Group = jQuery('#latest-cst-query > .elementor-widget-container > .elementor-posts');
const cf_div = document.createElement("a");
const ch_p = document.createElement("a");
const cp_a = document.createElement("a");
for (cards of card_Group) {
let cardChild = cards.children;
for (card of cardChild){
if (card.classList.contains("type-freebie")){
cf_div.classList.add("freebie_tax", "freebie_fcol");
cf_div.innerText = "Freebie";
card.firstElementChild.append(cf_div);
} if (card.classList.contains("type-hack")){
ch_p.classList.add("freebie_tax", "freebie_hcol");
ch_p.innerText = "Hack";
card.firstElementChild.append(ch_p);
} if (card.classList.contains("type-post")) {
cp_a.classList.add("freebie_tax");
cp_a.innerText = "Blog";
card.firstElementChild.append(cp_a);
}
}
}
The issue here is that you create only one element per group and when appended second time it actually being moved instead.
So what you need to do is create new element before it's appended.
for (card of cardChild){
if (card.classList.contains("type-freebie")){
let cf_div = document.createElement("a");
cf_div.classList.add("freebie_tax", "freebie_fcol");
cf_div.innerText = "Freebie";
card.firstElementChild.append(cf_div);
}
You can even clone it too:
const cf_div = document.createElement("a");
cf_div.classList.add("freebie_tax", "freebie_fcol");
cf_div.innerText = "Freebie";
for (card of cardChild){
if (card.classList.contains("type-freebie")){
card.firstElementChild.append(cf_div.cloneNode(true));
}

Javascript Vanilla - Double Click event handler on inputs / GetElementsByTagName

Problem: Can't assign a double-click event handler to my generated inputs; is this feasible using the getElementsByTagName?
Thanks for any help
Here is the code:
Generated inputs
function list_tasks() {
let container = document.getElementById("todo");
container.innerHTML = ""
if (task_array.length > 0) {
for (let i = 0; i < task_array.length; i++) {
let input = document.createElement("input");
input.value = task_array[i];
input.classList.add("record");
input.disabled = true;
container.appendChild(input);
}
}
}
Attaching the event
document.getElementsByClassName("record").addEventListener("dblclick", editTask);
And the console.log is never called
function editTask(e){
console.log("double click")
}
Update
Trying to loop across the array, but still, no double click event is fired
let record = document.getElementsByClassName("record");
for(var i = 0; i <= record.length; i++){
document.getElementsByClassName("record")[i].addEventListener("dblclick", editTask);
}
getElementsByClassName returns a nodes list i.e. an array. To access the element you need to get the value form the array.
Try this:
document.getElementsByClassName("record")[0].addEventListener("dblclick", editTask);
This should work.
The reason why this doesn't work is because you are marking the inputs as disabled. Disabled inputs don't react to some events, and looks like double-click is one of them.
Also, as #Royson wrote, getElementsByClassName() returns a list of multiple elements. If you want to add an event listener to all of them you have 2 options:
The best one IMO, if possible, is to attach it while creating the elements in list_tasks() function:
let input = document.createElement("input");
input.value = task_array[i];
input.classList.add("record");
input.disabled = true;
input.addEventListener("dblclick", editTask); // <--- here
container.appendChild(input);
If this is not possible to due scopes being inaccessible, you just loop over the result of getElementsByClassName():
Array.from(document.getElementsByClassName("record")).forEach(el => el.addEventListener("dblclick", editTask));
Edit: The spec says that "click" events should be disabled on a disabled input. Event though double-click isn't specified directly, my guess is that it's implied by it being a click too, or it requires click to be enabled so it can catch two fast ones.
https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls%3A-the-disabled-attribute
The way to do it is to create items as list items and then make contentEditable on the list items on double click B-)

Javascript click on specific element always uses wrong/other element id

I have several html pages and each one has a varying number of buttons that appear based on the page's content.
In just Javascript (since I don't use jquery), I am trying to have the same few lines of code apply to the respective button that was clicked, with the exception that the id tag has to be 'concatenated' into a variable based on the respective button that was clicked.
I saw other solutions on here that cycled through the elements of the class (in this case the "zoom_buttonClass"). However, when I attempt this, regardless of number of buttons on the page or which button was clicked, it is always the LAST button in the list that seems to be the one seen as clicked.
I need to always check if buttons are clicked, but how do I apply the actions based on the ACTUAL button that was clicked?
My HTML and JS code snippets are below:
Thanks in advance.
HTML code:
<div class="modalClass" id="myModalID">
<span class="closeModalClass" aria-label="Close Photo Enlargement Modal Box">×</span>
<img class="modal_contentClass" id="modalEnlargementID">
</div>
<div id="captionID"></div>
JS code:
for (var i = 0;i < document.getElementsByClassName("zoom_buttonClass").length;i++){
document.getElementsByClassName("zoom_buttonClass")[i].addEventListener('click', myFunction
(var attribute = this.getAttribute("id");
var photoIDstring = "photo"+counterX+"ID";
document.getElementById('myModalID').style.display = "block";
document.getElementById('captionID').style.display = "block";
document.getElementById("modalEnlargementID").src = document.getElementById(photoIDstring).src;
captionText.innerText = document.getElementById(photoIDstring).alt;
), false);
};
Well, I started again and I think I may have hit upon a solution. It seems to work.
var captionText = document.getElementById("captionID");
var howManyButtons = document.getElementsByClassName("zoom_buttonClass").length;
var buttonCollection = document.getElementsByClassName("zoom_buttonClass");
for (let i=0; i < howManyButtons; i++) {
buttonCollection[i].onclick = function() {
let photoIDstring = "photo"+(i+1)+"ID";
document.getElementById('myModalID').style.display = "block";
document.getElementById('captionID').style.display = "block";
document.getElementById("modalEnlargementID").src = document.getElementById(photoIDstring).src;
captionText.innerText = document.getElementById(photoIDstring).alt;
}
}

Populate select dropdown

I want to populate a select dropdown when the user click in the select. I am trying this, but apparently the click handler is only activated when the user click in the options, but in my case i don't have options. Here is a demo
$('select').click(function () {
var currentId = $(this).attr('id');
alert(currentId);
var total = $('.total').text();
for (i = 0; i <= total; i++) {
$('<option>').val(i).text(i).appendTo('#' + currentId);
}
});
Try this :As you are appending options for every click and hence you are not able to see the options. You can use .one() to populate options only for the first click and for second time click it will show you the populated options. Also use this
to append options instead of getting id of select box and use it.
$('select').one("click",function () {
var total = parseInt($('.total').text());
for (i = 0; i <= total; i++) {
$('<option>').val(i).text(i).appendTo(this);//use this to append
}
});
JSFiddle Demo
You can try to populate the select on mouse over, it could save you some time and give you more accessibility. Sometimes adding options on click just prevents the select from opening when it's supposed to, which can lead to frustrating the user...

How to get all radio buttons inside a table?

i have group of radio buttons inside a table along with other inputs in this table, and i can't get the radio buttons by id or name or by tag name, i want to get them by type if this possible, because id and name is auto generated by JSF, and i don't want to change this.
so the requirement is: get all radio buttons only (not all inputs) inside the table so i can loop on them.
I hope there's only one set of radio buttons. In that case something like this can help:
var inputs = yourForm.elements;
var radioes = [];
for (var i = 0; i < inputs.length; ++i) {
if (inputs[i].type == 'radio') {
radioes.push(input[i]);
}
}​
In case there are more than one set of radio buttons, better approach would be to have a dictionary with the name as the key and the value as the array of radio buttons in the group.
I know the question doesn't mention jQuery, but I just wanted to demonstrate the simplicity of it; please consider this more of a selling point of "hey, look how nice jQuery is".
// look how pretty I am
var radioButtons = $('table#MyTableId input[type="radio"]');
Given your form name is yourForm ... (<form name = "yourForm">) ... an easy way is just to parse all form elements and check for the type, something like this:
var your_namespace = {};
your_namespace.your_function = function() {
var l = document.yourForm.elements.length;
for (i=0; i<l; i++) {
var type = yourForm.elements[i].type;
if (type=="radio") alert("Form element " + i + " is a radio!");
}
}
Instead of alerting you can ofc do alot of other things with the element id.
You can have this way easier when using a framework like MooTools or JQuery.

Categories