onclick for all buttons with a data attribute - javascript

I have three different boxes which all have a button inside.
I want to register an onclick event for every button with a specific data attribute.
I've started a jsfiddle, any suggestions?
http://jsfiddle.net/mwils/w6gofb30/
var buttons = document.querySelectorAll("button[data-donation]");
for (i = 0; i < buttons.length; i++) {
document.querySelectorAll("button[data-donation]")[i].onclick = console.log(buttons[i]);
}
function letsGo(init) {
var input = document.body.querySelector("input[name='amount']"),
amount = parseInt(input.value, 10) * 100,
url = "https://donate.shelter.org.uk/b?cid=152&freeamount=1&amount=";
if (input.value === '' || parseInt(input.value, 10) >= 0) {
window.location.href = url + init * 100;
} else {
window.location.href = url + amount;
}
}
<div class="shopping-item">
<p><span>£33</span> could help a family find and keep a home</p>
<input type="number" name="amount" placeholder="£33" class="shopping-1-input">
<button data-donation="33" class="donate-button shopping-1-button">donate now</button>
</div>
<div class="shopping-item">
<p><span>£50</span> could help a family find and keep a home</p>
<input type="number" name="amount" placeholder="£50" class="shopping-2-input">
<button data-donation="50" class="donate-button shopping-2-button">donate now</button>
</div>
<div class="shopping-item">
<p><span>£188</span> could help a family find and keep a home</p>
<input type="number" name="amount" placeholder="£188" class="shopping-3-input">
<button data-donation="188" class="donate-button shopping-3-button">donate now</button>
</div>

While there are really many ways to do that i, most of the time, prefer a simple click delegation. I set that on elements that contain the buttons(could be the window or document.body itself).
To make it simple i show you that with a container first.
<div id="container">
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
</div>
here is the js
document.getElementById('container').onclick=function(e){
if(e.target.nodeName=='BUTTON'){
alert(e.target.textContent);
}
}
What does this???
if i click on the container it checks if the target element is a button.
if yes it alerts the textContent of the button. simple right?
Doing so you avoid alot of extra variables.
In your case
document.onclick=function(e){
if(e.target.dataset['donation']){
alert(e.target.dataset['donation']);
}
}
shorter
document.onclick=function(e){
e=e.target.dataset; // reuse the variable e
!e['donation']||alert(e['donation']);
}
using
<button data-donation="33">donate now</button>
DEMO's
http://jsfiddle.net/j8xgqfmk/
Extra
button text followed by $ symbol done with css
http://jsfiddle.net/j8xgqfmk/1/
preceded by a £
http://jsfiddle.net/j8xgqfmk/2/
but returning the desidered value
Why?
No loops!!!
Only one event handler!!!!
Less variables!!!
Short code !!!
Faster then multiple complex eventhandlers.
other considerations:
var buttons = document.querySelectorAll("button[data-donation]");
for (i = 0; i < buttons.length; i++) {
document.querySelectorAll("button[data-donation]")[i].onclick=console.log(buttons[i]);
}
Should be written
var buttons=document.querySelectorAll('button[data-donation]'),
btnlength=buttons.length; //cache variables
for (var i = 0; i < btnlength; i++) { // var i
buttons[i].onclick=function(){ //buttons is already defined
console.log(this);
}
// and you need to pass a function to the onclick event
}
or even better
function handler(e){
console.log(this,e);
}
var btns=document.querySelectorAll('button[data-donation]'),
l=btns.length;
while(l--)btns[l].addEventListener('click',handler,false);
or the "Haters gonna hate" version
http://jsfiddle.net/Lbk1ueme/
var B=document.querySelectorAll('button[data-donation]'),
L=B.length,
I=0,
H=function(){console.log(this)};
for(;I<L;B[I++].onclick=H);
https://stackoverflow.com/a/21353032/2450730
if you have difficulties to understand that i can help you to write it based on your requests. For any other question just ask.
keep stuff simple

Loop through your element array (as you already are doing) and use addEventListener to bind the click event to each...
var buttons = document.querySelectorAll("button[data-donation]");
for (i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function(event) {
alert(this.getAttribute('data-donation')); // alert the value of data-donation attribute
});
}
<div class="shopping-item">
<p><span>£33</span> could help a family find and keep a home</p>
<input type="number" name="amount" placeholder="£33" class="shopping-1-input">
<button data-donation="33" class="donate-button shopping-1-button">donate now</button>
</div>
<div class="shopping-item">
<p><span>£50</span> could help a family find and keep a home</p>
<input type="number" name="amount" placeholder="£50" class="shopping-2-input">
<button data-donation="50" class="donate-button shopping-2-button">donate now</button>
</div>
<div class="shopping-item">
<p><span>£188</span> could help a family find and keep a home</p>
<input type="number" name="amount" placeholder="£188" class="shopping-3-input">
<button data-donation="188" class="donate-button shopping-3-button">donate now</button>

The problem is you are not attaching events correctly to the element. You are assigning console.log and not a function to it. Seconds querySelectorAll is expensive. You should not keep looking things up in a loop. Create a variable and store the live html node collection it returns into it.
var btns = document.querySelectorAll("button[data-donation]");
for (var i=0; i<btns.length; i++) {
btns[i].onclick = function() {
console.log(this);
console.log(this.getAttribute("data-donation"));
}
}
function letsGo(init) {
var input = document.body.querySelector("input[name='amount']"),
amount = parseInt(input.value, 10) * 100,
url = "https://donate.shelter.org.uk/b?cid=152&freeamount=1&amount=";
if (input.value === '' || parseInt(input.value, 10) >= 0) {
window.location.href = url + init * 100;
} else {
window.location.href = url + amount;
}
}
<div class="shopping-item">
<p><span>£33</span> could help a family find and keep a home</p>
<input type="number" name="amount" placeholder="£33" class="shopping-1-input">
<button data-donation="33" class="donate-button shopping-1-button">donate now</button>
</div>
<div class="shopping-item">
<p><span>£50</span> could help a family find and keep a home</p>
<input type="number" name="amount" placeholder="£50" class="shopping-2-input">
<button data-donation="50" class="donate-button shopping-2-button">donate now</button>
</div>
<div class="shopping-item">
<p><span>£188</span> could help a family find and keep a home</p>
<input type="number" name="amount" placeholder="£188" class="shopping-3-input">
<button data-donation="188" class="donate-button shopping-3-button">donate now</button>
</div>

You've got the selector right already, i.e. var buttons = document.querySelectorAll("button[data-donation]") is pulling in all the buttons having data-donation, but there's no need to do another querySelectorAll inside your loop, you just need to reference each button in your result with buttons[i].
Your other mistake is setting onclick = console.log(), instead of onclick = function() { console.log() }. So all you need to change to get it working is:
var buttons = document.querySelectorAll("button[data-donation]");
for (i = 0; i < buttons.length; i++) {
var button = buttons[i];
buttons[i].onclick = function() { console.log(button) };
}
http://jsfiddle.net/w6gofb30/4/

document.querySelectorAll("button[data-donation]").every(function(e,i,a){
e.addEventListener("click", function(){
console.log(this);
console.log(this.getAttribute("data-donation"));
}, false);
});
but before this u need:
NodeList.prototype.every = function(f){
for(var k=0;k<this.length;k++)
f(this[k],k,this);
}
http://jsfiddle.net/sa0ezut7/

Related

Generate Dynamic Inputs with Unique Names in jQuery

In my HTML form, it's possible to add additional inputs dynamically by clicking a button. I've got this part to work, however I need each input to have a unique name.
This is my code so far,
<div class="control-group field_wrapper">
<label class="control-label"><strong> Phone Number 1</strong></label>
<input type="text" class="input-medium" name="phone_number[]">
<button class="btn btn-success add-number" type="button" title="Add">Add</button>
</div>
<div class="additionalNumber"></div>
My JS as below,
$(document).ready(function(){
var maxField = 10;
var addButton = $('.add-number');
var wrapper = $('.additionalNumber');
function fieldHTML(inputNumber) {
return `<div class="control-group field_wrapper">\
<label class="control-label"><strong> Phone Number ${inputNumber}</strong></label>\
<input type="text" class="input-medium" name="phone_number[${inputNumber}]">\
<button class="btn btn-danger remove" type="button">Remove</button>\
</div>`;
}
var x = 1;
$(addButton).on('click', function(e) {
if (x < maxField) {
x++;
$(wrapper).append(fieldHTML(x));
}
if (x >= maxField) {
alert('Limited to 10.');
}
});
$(wrapper).on('click', '.remove', function(e) {
e.preventDefault();
$(this).parents('.control-group').remove();
x--;
});
});
Using this code, I can get unique name for each input which are created by dynamically. But my problem is name[x] index not works properly when it is removing. That mean, just think I have added 3 input and delete second one and again I am adding new one, then it has same name twice. In this case, it is phone_number[3] for second input and phone_number[3] for thirt one also.
This is the fiddle from above code. Any help is appreciated.
You don't need to index the inputs for PHP either - 3x inputs named phone_number[] will automatically be indexed 0 - 2 on the back end:
<input type="text" name="phone_number[]">
<input type="text" name="phone_number[]">
<input type="text" name="phone_number[]">
[phone_number] => Array
(
[0] => a
[1] => b
[2] => c
)
That doesn't help with your plain text Phone Number n label though. And maybe you have your own reasons to want an input name index.
If you think about it, if you're going to allow deletions of any item in the list, and you need the results to be sequential, the only option is to renumber everything each time you make a change. You don't need to include any numbering when you add a new element, just regenerate all numbering.
Here's a working snippet doing that. My changes:
No need to pass the count x to fieldHTML(), we're going to renumber everything after you add the element;
Add a <span class='count'></span> in your label, which we can target and update;
Add a reNumber() function which will iterate over all inputs on the page and number them sequentially;
Call that function after any change;
Notes:
The 2 separate tests if (x < maxField) and if (x >= maxField) can be combined into a single if/else;
If you want to get rid of the duplication of your HTML block, you could give the first one an id like template, and then instead of duplicating that HTML in your JS, just copy the template, eg :
let $copy = $('#template').clone();
wrapper.append($copy);
wrapper and addButton are already jQuery objects, no need to wrap them with $() a second time to use them;
If you do want to number your input names, for consistency the first should probably be phone_number[1];
$(document).ready(function() {
var x = 1;
var maxField = 10;
var addButton = $('.add-number');
var wrapper = $('.additionalNumber');
function fieldHTML() {
return `<div class="control-group field_wrapper">\
<label class="control-label"><strong> Phone Number <span class='count'></span></strong></label>\
<input type="text" class="input-medium" name="phone_number[]">\
<button class="btn btn-danger remove" type="button">Remove</button>\
</div>`;
}
/**
* Iterate over all inputs and renumber sequentially
*/
function reNumber() {
let count;
wrapper.find('.field_wrapper').each(function (i) {
// .each() index is 0-based, and input #1 is already on the page,
// so extras start at #2
count = i + 2;
$('.count', $(this)).html(count);
// If you want to index your input names, but you can safely leave
// this out, PHP will index them anyway
$('input', $(this)).attr('name', 'phone_number[' + count + ']')
});
}
addButton.on('click', function(e) {
if (x < maxField) {
x++;
wrapper.append(fieldHTML());
reNumber();
} else {
alert('Limited to 10.');
}
});
wrapper.on('click', '.remove', function(e) {
e.preventDefault();
$(this).parents('.control-group').remove();
x--;
reNumber();
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="control-group field_wrapper">
<label class="control-label"><strong> Phone Number 1</strong></label>
<input type="text" class="input-medium" name="phone_number[]">
<button class="btn btn-success add-number" type="button" title="Add">Add</button>
</div>
<div class="additionalNumber"></div>

How to dynamically keep track of user inputs in JavaScript?

Does anyone have any idea to dynamically keep track of user inputs in a form? I learned how to disable a button and if users want to enable it, they would just have to fill in the input fields. While this works, if a user decides to backspace and go back to a clear field, the button is still enabled. I wanted to get some insight or ideas to keep track of user inputs dynamically.
I'm a bit new to JS so I just wanted some ideas. Is it possible to use for loops/forEach methods to iterate through the input fields? Or what approach do you recommend on taking?
HTML:
<form class="container">
<input type="text" class="input" />
<input type="email" class="input" id="input" />
<button type="submit" id="submitButton" href="index.html" disabled>
Submit
</button>
</form>
JavaScript:
document.getElementById("input").addEventListener("keyup", function() {
var inputs = document.querySelectorAll("input");
if (inputs != "") {
document.getElementById("submitButton").removeAttribute("disabled");
} else if ((inputs = "")) {
document.getElementById("submitButton").setAttribute("disabled", null);
}
});
Here is the solution of your problem.
document.addEventListener("keyup", function() {
var inputs = document.querySelectorAll("input");
var emptyFillExists = false;
for (let index = 0; index < inputs.length; index++) {
if (inputs[index].value.length === 0) {
emptyFillExists = true;
break;
}
}
if (!emptyFillExists) {
document.getElementById("submitButton").removeAttribute("disabled");
} else {
document.getElementById("submitButton").setAttribute("disabled", null);
}
});
<form class="container">
<input type="text" class="input" />
<input type="email" class="input" />
<button type="submit" id="submitButton" href="index.html" disabled>
Submit
</button>
</form>
There are a few things wrong with your codes:
You assume inputs as strings. It isn't. It's an array.
You track keyup event for only 1 input. You should track keyup event for all inputs instead.
Here's what I'd suggest you do:
Add event listener keyup for the form.
Interate through each input and check.
function areInputsValid() {
// Iterate through every input and check its value
var inputs = document.querySelectorAll('input');
for (var i = 0; i < inputs.length; i++)
if (inputs[i].value == '')
return false;
return true;
}
// Get the form element
var form = document.getElementsByTagName('form')[0];
// Add event listener
form.addEventListener('keyup', function() {
// Are the inputs valid?
if (areInputsValid())
document.getElementById("submitButton").removeAttribute("disabled");
else
document.getElementById("submitButton").setAttribute("disabled", null);
})
EDIT: as charlietfl pointed out, there are bugs in my previous answer.

Javascript: Triggering "for" loop using "if...else" statements not working

I am trying to make a simple script which automatically blocks the input boxes in the file when I tick a checkbox.
For this, I am trying to add/remove the "disabled" attribute by triggering a loop every time the checkbox is clicked. Looks something like this:
function locker() {
var boxes = document.querySelectorAll("input[type='text']");
var x = getElementById("lock")
for (i = 0; i < inputBoxes.length; i++) {
if (x.checked == true) {
boxes[i].disabled = true;
} else {
boxes[i].disabled = false;
}
}
}
<input type="checkbox" id="lock" onClick="locker()">
<input type="text"></input>
<input type="text"></input>
<input type="text"></input>
However, I can't seem to get it to work. I don't have much experience coding, and I feel like I am making a very basic mistake, but I couldn't find a solution to this problem so far... How can I solve this? Are there any other workarounds to get the same result?
Thanks in advance
You need to use document.getElementById("lock") instead of getElementById("lock") and use the correct variable names for your variables. You used inputBoxes and boxes while you meant to use the same variable.
function locker() {
var inputBoxes = document.querySelectorAll("input[type='text']");
var x = document.getElementById("lock")
for (i = 0; i < inputBoxes.length; i++) {
if (x.checked == true) {
inputBoxes[i].disabled = true;
} else {
inputBoxes[i].disabled = false;
}
}
}
<input type="checkbox" id="lock" onClick="locker()">
<input type="text"></input>
<input type="text"></input>
<input type="text"></input>

How put regex in getElementByIds

Suppose to have more buttons:
<input type="button" id="number_1" value="A">
<input type="button" id="number_2" value="B">
<input type="button" id="number_n" value="N">
I want get click when the user click on them. So I would use something like this code (without Jquery):
var button=document.getElementById('[id^=number]');
button.onclick=function(){alert("HIII")};
But this code is not work. Anyone can help?
Attribute selectors
document.querySelectorAll()
Array.prototype.forEach()
Function.prototype.call()
EventTarget.addEventListener()
Array.prototype.forEach.call(document.querySelectorAll('[id^=number_]'), function (element) {
element.addEventListener('click', function (e) {
alert(e.target.id);
});
});
<input type="button" id="number_1" value="A">
<input type="button" id="number_2" value="B">
<input type="button" id="number_n" value="N">
'[id^=number]'
That's an attribute selector, not an ID and not a regular expression. You shouldn't pass it to getElementById.
You can use selectors with querySelectorAll
var buttons = document.querySelectorAll('[id^=number]');
but that returns a NodeList, not a single element, so you have to loop over it:
for (var i = 0; i < buttons.length; i++) {
var button = buttons[i];
button.onclick=function(){alert("HIII")};
}
and there is no point in creating a new function each time you go around the loop, create one and reuse it:
for (var i = 0; i < buttons.length; i++) {
var button = buttons[i];
button.onclick = myFunction;
}
function myFunction () {
alert("HIII");
}

JS - Attach the same function to many html forms

I am trying to make an e-commerce-like webpage (for practice) wherein a click on any of the buttons would update the cart value by the number (quantity) specified on the input element.
So far, I was only able to update the cart from the first form because when I try to assign the function on every form using a loop, the cart updates for a millisecond then returns to zero. I assume its because of the scope.
I know there's an easier way to do this without manually assigning the function for every document.forms[n]
JS
window.onload = function()
{
var getForm = document.forms[0];
var numItems = 0;
getForm.onsubmit = function(event)
{
event.preventDefault();
var getInput = getForm.elements["num-item"].value;
if(parseInt(getInput))
{
numItems = numItems + parseInt(getInput);
var getCart = document.getElementById("item-count");
getCart.innerHTML = numItems;
getForm.reset();
}
else
{
alert("Please enter a valid number");
}
}
HTML
Cart:
<div class="basket">
<p><i class="fa fa-shopping-basket"></i></p>
<p id="item-count">0</p>
</div>
HTML Form: For brevity, I'm only posting 1 form example, but in reality, I have 6 other forms that are exactly the same.
<div class="buy-config">
<form class="buy-form" name="buy-form">
<label>Quantity:</label>
<input type="text" class="num-item" />
<button class="buy-btn">Add to Cart</button>
</form>
</div>
Loop through all of the forms by querying the selector (using whatever method you prefer, depending on performance requirements and markup flexibility -- I've used getElementsByClassName) and executing a for loop.
Inside the loop, bind a function to the "submit" event using addEventListener. You can define the function in-line (as I've done), or define the function elsewhere, assign it to a variable, and reference the variable when binding to the event.
Within the event listener function, you will refer to the form that was submitted as this.
On top of the changes described above, I've made some minor changes to your code:
Your previous version was overwriting the contents of the cart each time. This may have been on purpose, depending on whether you have one "basket" for each item or one overall (this wasn't clear in the question). So, rather than initialize numItems to zero, I've initialized it to the current number of items in the cart.
Consider using input type="number" HTML form elements. They're supported by nearly every browser and only accept digits -- they also have up/down arrows and can be set with the scroll wheel. On browsers that don't support them, they fall back to a basic text input.
var forms = document.getElementsByClassName("buy-form");
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener("submit", function(event) {
event.preventDefault();
var numItems = parseInt(document.getElementById("item-count").innerHTML);
var getInput = this.getElementsByClassName("num-item")[0].value;
if (parseInt(getInput)) {
numItems = numItems + parseInt(getInput);
var getCart = document.getElementById("item-count");
getCart.innerHTML = numItems;
this.reset();
} else {
alert("Please enter a valid number");
}
});
}
<div class="basket">
<p><i class="fa fa-shopping-basket"></i></p>
<p id="item-count">0</p>
</div>
<div class="buy-config">
<form class="buy-form" name="buy-form">
<label>Quantity:</label>
<input type="number" class="num-item" />
<button class="buy-btn">Add to Cart</button>
</form>
</div>
<div class="buy-config">
<form class="buy-form" name="buy-form">
<label>Quantity:</label>
<input type="number" class="num-item" />
<button class="buy-btn">Add to Cart</button>
</form>
</div>
<div class="buy-config">
<form class="buy-form" name="buy-form">
<label>Quantity:</label>
<input type="number" class="num-item" />
<button class="buy-btn">Add to Cart</button>
</form>
</div>
You can use the jQuery selector.
<script src="http://code.jquery.com/jquery-1.12.0.min.js"></script>
<script>
$(document).ready(function() {
$('.buy-btn').click(function(){
$(this).parent('form').submit();
});
});
</script>
<form class="buy-form">
<label>Quantity:</label>
<input type="text" class="num-item" />
<button class="buy-btn">Add to Cart</button>
</form>
The code above will setup a function for each HTML elements that has the css class buy-btn.
You can select anything using parent, children, prev, next or find function from jQuery.
Of course this is just a basic exemple I'm showing here, and again some simple example could be :
$('.buy-btn').click(function(){
$(this).parent('form').submit();
//var itemCount = $('#item-count').html();
//itemCount++;
//$('#item-count').html(itemCount);
var numItem = $(this).prev('.num-item').val();
$('#item-count').html(numItem);
});
Unfortunately, you're going to have to loop through the elements in your JavaScript and assign the function to each, however you can do it a bit simpler with some querySelector methods thrown in:
window.onload = function() {
var getCart = document.getElementById('item-count');
var forms = document.querySelectorAll('.buy-form');
var numItems = 0;
var isNum = function(n) {
return(!isNaN(parseFloat(n)) && isFinite(n));
};
var handler = function(e) {
(e || event).preventDefault();
var getInput = this.querySelector('.num-item').value;
if(isNum(getInput)) {
numItems += parseInt(getInput);
getCart.innerHTML = numItems;
this.reset();
} else {
alert("Please enter a valid number");
}
};
for(var i = 0, len = forms.length; i < len; i++) {
forms[i].addEventListener('submit', handler);
}
};

Categories

Resources