I'm trying to make a copy of the original container div with the cloneNode method in javascript inside the container there are 3 buttons with btn class, when I make a copy of the original one the last only the last element in the copied item is only printing hello in the console, any ideas?
let add = document.querySelector('.add-button');
const item = document.querySelector('.container');
let btn = document.querySelectorAll('.btn');
add.addEventListener('click', function() {
makecopy();
});
btn.forEach(el => {
el.addEventListener('click', function() {
console.log("hello")
})
});
function makecopy() {
let copiedItem = item.cloneNode(true);
item.parentNode.insertBefore(copiedItem, item);
}
<div class="add-panel">
<button type="button" class="add-button">Create new</button>
</div>
<div class="container">
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
You are only setting up event listeners on the first set of buttons, not the cloned ones. Instead of setting up listeners on each button, use "event delegation" to allow the event to "bubble" up to a common ancestor and handle the event there. This way, all the newly added elements will immediately work without needing their own handler and there is only one handler that needs to be set up instead of many.
You've also got some redundant code and code that will no longer be needed when you take this approach.
// No need to set up an anonymous handler that calls the real one. Just
// register the real one
document.querySelector('.add-button').addEventListener('click', makecopy);
const item = document.querySelector('.container');
function makecopy() {
let copiedItem = item.cloneNode(true);
item.parentNode.insertBefore(copiedItem, item);
}
// Listen for clicks on the document:
document.addEventListener('click', function(event) {
// Check to see if it was a button that was clicked:
if(event.target.classList.contains("btn")){
console.log("hello");
};
});
<div class="add-panel">
<button type="button" class="add-button">Create new</button>
</div>
<div class="container">
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
</div>
Just appears you weren't adding new listeners when you made new buttons. Some adjustments.
let add = document.querySelector('.add-button');
const item = document.querySelector('.container');
let btn = document.querySelectorAll('.btn');
add.addEventListener('click', function() {
makecopy();
});
btn.forEach(el => {
el.addEventListener('click', function() {
console.log("hello")
})
});
function makecopy() {
let copiedItem = item.cloneNode(true);
item.parentNode.insertBefore(copiedItem, item);
copiedItem.addEventListener('click', function() {
console.log("hello")
})
}
<div class="add-panel">
<button type="button" class="add-button">Create new</button>
</div>
<div class="container">
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
Related
I wanted to log the elements of the clicked button to the console, but it only worked for "CLICK ME 1." It did not work for another button.
var testLog = (function(){
var getButton = document.querySelector('.button__');
getButton.addEventListener('click', checkPlease);
function checkPlease(event){
console.log(event.target);
}
})();
<section class="product">
<div class="product__wrapper">
<div class="item">
<button class="button__">CLICK ME 1</button>
<button class="button__">CLICK ME 2</button>
</div>
</div>
</section>
Please use querySelectorAll to get all the elements and then forEach on the returned nodelist to attach addEventListener to each element
var getButtonList = document.querySelectorAll('.button__');
getButtonList.forEach(el => el.addEventListener('click', checkPlease));
you get only first button, because of document.querySelector in your code. Use document.querySelectorAll instead
Attach your event listener to all elements, using loop, like this:
var getButton = document.querySelectorAll('.button__');
getButton.forEach(el => el.addEventListener('click', checkPlease))
Try this: you need to use querySelectorAll:
var getButton = document.querySelectorAll(".button__");
if (getButton && getButton.length > 0) {
getButton.forEach((element) => {
element.addEventListener("click", checkPlease);
});
}
function checkPlease(event: any) {
console.log(event.target);
}
Instead of adding a listener per button, you can use event delegation by adding a single listener to the parent element and differentiating by target details.
const testLog = (function(){
const buttonContainer = document.querySelector('.item');
buttonContainer.addEventListener('click', checkPlease);
function checkPlease(event){
console.log(event.target);
}
})();
<section class="product">
<div class="product__wrapper">
<div class="item">
<button class="button__">CLICK ME 1</button>
<button class="button__">CLICK ME 2</button>
</div>
</div>
</section>
On the click of addField_1 button, I have to clone the container div(id = 'projection') and append it right below it.
I am doing this in jquery like this:
$('[id^="addField"]').click(() => {
var containerDiv = $(this).parent();
console.log(containerDiv.attr('id'));
containerDiv.after(containerDiv.clone());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="row" id="projection" >
<select class="form-control" aria-placeholder="Select field"></select>
<button type="button" class="btn btn-light" id="addField_1" name="mybutton">+</button>
</div>
There is no error when the button is clicked. But the div is not being copied. I also tried to log the parent div's id. It is also undefined. Please tell me what am I doing wrong.
Notice that you have used arrow function because of which this is referring to the window.
There are two solutions for it :
1. Using event.target in the arrow function to get the targeted element.
$('[id^="addField"]').click((event) => {
let containerDiv = $(event.target).parent();
console.log(containerDiv.attr('id'));
containerDiv.after(containerDiv.clone());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="row" id="projection" >
<select class="form-control" aria-placeholder="Select field"></select>
<button type="button" class="btn btn-light" id="addField_1" name="mybutton">+</button>
</div>
2. To not use arrow function but anonymous function and use this :
$('[id^="addField"]').click(function() {
let containerDiv = $(this).parent();
console.log(containerDiv.attr('id'));
containerDiv.after(containerDiv.clone());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="row" id="projection" >
<select class="form-control" aria-placeholder="Select field"></select>
<button type="button" class="btn btn-light" id="addField_1" name="mybutton">+</button>
</div>
With this keyword, you will reference to Window object.
That why you should use something like that:
$('[id^="addField"]').on('click', event => {
const parentDiv = $(event.target).closest('.row');
console.log(parentDiv.html());
});
I have a button as such whose id I am not aware of
<button id="" class="mybtn" type=""></button>
and now I want to get the id of this button on click
("//what to write here").click(){
console.log($(this).id);//something like this i want
}
but the problem with using class selector is that I have multiple buttons which so it will select all of them and not just the one which is clicked.
You can do it like this. I commented the code for the syntax
$("button.mybtn").on("click", function() {
console.log($(this).attr("id")); // return blank when no id
console.log(this.id); // return undefined when no id
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="hello" class="mybtn" type="">Sample</button>
<button id="hello1" class="mybtn" type="">Sample</button>
The event handler below is attached to all buttons (elements) with the class .btn but since you can only click one button at a time, you will only see one id per click - the id of the button clicked:
$('.mybtn').on('click', function() {
console.log( this.id );
});
$(function() {
$('.mybtn').on('click', function() {
console.log( this.id );
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="id1" class="mybtn" type="">Button 1</button>
<button id="id2" class="mybtn" type="">Button 2</button>
<button id="id3" class="mybtn" type="">Button 3</button>
<button id="id4" class="mybtn" type="">Button 4</button>
I'm encountering a typical situation while accessing the innerHTML property using jQuery. I've fetched the target button using the jQuery attribute selector.
Below is the snippet of jQuery attribute selector.
jQuery('button[type="button"][class="btn btn-primary"]').each(function () {
var btn = jQuery(this);
console.log(btn);
if (btn[0].innerHTML === "OK") {
console.log("ok");
jQuery(this).click();
}
});
Following is the screenshot of the console log of the target button. It's innerHTML property is set to OK.
Following is the screenshot of the value of the innerHTML while debugging the target button object. In this case the value is "".
Ideally, the values of the innerHTML should be the same for both the cases.
EDIT
Why does this behavior differ that the ideal one? For both of the cases, the value of the innerHTML should be the same.
Also, there were multiple buttons. I have taken screenshots of different buttons. Thus their ID's are different. But still, the behavior is same.
Try something like this.
function SomeEvent($ele) {
alert($ele.html());
return false;
}
function invokeBtnEvents() {
$("[data-action=some-event]").off(); // clear old events
$("[data-action=some-event]").on("click", function() {
var $this = $(this);
return SomeEvent($this);
}) // define event(s)
return false;
}
$(document).ready(function() {
invokeBtnEvents();
});
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-12">
<br />
<button data-action="some-event" class="btn btn-primary">Button 1</button>
<button data-action="some-event" class="btn btn-primary">Button 2</button>
<button data-action="some-event" class="btn btn-primary">Button 3</button>
<button data-action="some-event" class="btn btn-primary">Button 4</button>
</div>
</div>
</div>
Your issue
What you are doing that I think is your main issue is the $.each() method.
Store the buttons in a variable,
let $button = $("button"); // this returns an array of all the buttons on the DOM
You can then use the $.each() method to get the clicked element
$.each($button, function(index, btn){
const $this = $(btn);
$this.off();
$this.click(function(){someEvent($this)});
});
I would not invoke button clicks like this because every time you click a button, this each loop gets ran. It will then send all of the buttons to that action unless you parse by an ID or something (you are using the innerText).
If you use the code in my snippet, only the clicked button will be triggered.
An alternative approach to my first snippet is using something like a dispatcher.
function DoActionOneClick($ele){
alert("Action 1 " + $ele.html());
}
function DoDefaultClick($ele){
alert("Default Action " + $ele.html());
}
function DispatchEvent(action, $ele){
switch(action){
case "some-event-1":
DoActionOneClick($ele);
break;
default:
DoDefaultClick($ele);
break;
}
}
function invokeActions(){
$("[data-action]").off();
$("[data-action]").on("click", function(){
// get this
var $this = $(this);
// get action
var action = $this.data().action;
DispatchEvent(action, $this);
});
}
$(document).ready(function(){
invokeActions();
})
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-12">
<br />
<button data-action="some-event-1" class="btn btn-primary">Button 1</button>
<button data-action="some-event-2" class="btn btn-primary">Button 2</button>
<button data-action="some-event-2" class="btn btn-primary">Button 3</button>
<button data-action="some-event-2" class="btn btn-primary">Button 4</button>
</div>
</div>
</div>
I have a number of buttons within a section, each with an id of the form #balls-left-n, where n ranges from 1 to 15.
When one of these buttons is clicked, I want to grab the number from the id that was clicked and hide all of the buttons with ids that have names including numbers that are greater than the one clicked on.
So, if #balls-left-13 is clicked, I want to hide #balls-left-14 and #balls-left-15. But if #balls-left-3 is clicked I want to hide all the buttons from #balls-left-4 through #balls-left-15.
I'm a novice at web-dev so if I've made other mistakes or taken a poor approach don't hesitate to point that out.
I have a handler for each of the buttons (which if I knew more could probably be one function) that look like this:
$("#balls-left-14").click(function() {
var num_balls = $(this).attr('id').match(/[\d]/);
j_end_balls_on_table = 14;
$("#balls-left button:gt(num_balls-2)").hide;
...
other stuff
...
});
This didn't work and I get an error that num_balls is undefined, which I don't understand.
#balls-left is the section all of the buttons are inside of.
relevant HTML as requested
<section id="balls-left">
<h2>How Many Balls are Left on the Table?</h2>
<button type="button" id="balls-left-2" class="x-balls-left">2</button>
<button type="button" id="balls-left-3" class="x-balls-left">3</button>
<button type="button" id="balls-left-4" class="x-balls-left">4</button>
<button type="button" id="balls-left-5" class="x-balls-left">5</button>
<button type="button" id="balls-left-6" class="x-balls-left">6</button>
<button type="button" id="balls-left-7" class="x-balls-left">7</button>
<button type="button" id="balls-left-8" class="x-balls-left">8</button>
<button type="button" id="balls-left-9" class="x-balls-left">9</button>
<button type="button" id="balls-left-10" class="x-balls-left">10</button>
<button type="button" id="balls-left-11" class="x-balls-left">11</button>
<button type="button" id="balls-left-12" class="x-balls-left">12</button>
<button type="button" id="balls-left-13" class="x-balls-left">13</button>
<button type="button" id="balls-left-14" class="x-balls-left">14</button>
<button type="button" id="balls-left-15" class="x-balls-left">15</button>
</section>
Hope this helps.
var exploded = id.split("-");
alert(exploded.pop());
Now, to use that concept on your HTML structure, you can do something like this:
$(document).ready(function() {
$(".x-balls-left").click(function(e) {
e.preventDefault();
var exploded = this.id.split("-");
alert(exploded.pop());
});
});
And here's a Fiddle you can play around with.
You might don't even need all of these if your elements to hide share the same parent. Just set class on click .selected and hide the rest using CSS .selected.x-balls-left ~ .x-balls-left {display: none;}
$('.x-balls-left').click(function() {
$(this).toggleClass('selected');
})
.selected.x-balls-left ~ .x-balls-left {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section id="balls-left">
<h2>How Many Balls are Left on the Table?</h2>
<button type="button" id="balls-left-2" class="x-balls-left">2</button>
<button type="button" id="balls-left-3" class="x-balls-left">3</button>
<button type="button" id="balls-left-4" class="x-balls-left">4</button>
<button type="button" id="balls-left-5" class="x-balls-left">5</button>
<button type="button" id="balls-left-6" class="x-balls-left">6</button>
<button type="button" id="balls-left-7" class="x-balls-left">7</button>
<button type="button" id="balls-left-8" class="x-balls-left">8</button>
<button type="button" id="balls-left-9" class="x-balls-left">9</button>
<button type="button" id="balls-left-10" class="x-balls-left">10</button>
<button type="button" id="balls-left-11" class="x-balls-left">11</button>
<button type="button" id="balls-left-12" class="x-balls-left">12</button>
<button type="button" id="balls-left-13" class="x-balls-left">13</button>
<button type="button" id="balls-left-14" class="x-balls-left">14</button>
<button type="button" id="balls-left-15" class="x-balls-left">15</button>
</section>
$(document).on('click', '.balls-left', function() {
var num = getNum(this);
$('.balls-left').each(function() {
var that = $(this);
var bnum = getNum(that);
if (bnum > num) {
that.show();
} else {
that.hide();
}
});
});
var getNum = function(elem) {
if (elem) {
return $(elem).attr('id').replace('balls-left-', '');
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="balls-left-1" class="balls-left">Ball 1</div>
<div id="balls-left-2" class="balls-left">Ball 2</div>
<div id="balls-left-3" class="balls-left">Ball 3</div>
<div id="balls-left-4" class="balls-left">Ball 4</div>
<div id="balls-left-5" class="balls-left">Ball 5</div>
$("#balls-left button:gt(num_balls-2)").hide;
This is an invalid CSS selector, and only gets the hide method, without calling it. You want something like:
$("#balls-left button:gt("+(num_balls-2)+")").hide();
First you should put a class on each object so you can reference them all at once, and the simplest way to understand is to just put the ball number right in the tag as a custom attribute if you can:
<input type="button" id="balls-left-1" class="left-ball" num="1"/>
<input type="button" id="balls-left-2" class="left-ball" num="2"/>
etc...
Then you can write the javascript as follows:
$('.left-ball').click(function () {
var BallNum = $(this).attr('num');
$('.left-ball').each(function () {
if ($(this).attr('num') > BallNum) {
$(this).hide();
}
});
});
You can use RegEx match like this. This might resolve your undefined num_balls error message.
$("#balls-left-14").click(function() {
var ret = $(this).attr('id').match("[0-9]+");
var num_balls = ret[0];
j_end_balls_on_table = 14;
$("#balls-left button:gt(num_balls-2)").hide;
...
other stuff
...
});
Another way of doing it using your original HTML:
$('.x-balls-left').click(function () {
var BallNum = $(this)[0].innerHTML;
$('.x-balls-left').each(function () {
if ($(this)[0].innerHTML > BallNum) {
$(this).hide();
}
});
});
I just did it like this:
$('button[id^=balls-left-]').click(function(){
var num_balls = $(this).attr('id').match(/[\d]/);
$('#balls-left button:gt(' + num_balls + ')').hide();
});
Keep in mind that :gt select by index, it means that $('#balls-left button:gt(2)') will not select the button with id balls-left-2 but the one with id balls-left-4 (according to the html you posted).