Trouble Repeating Button Functionality Across Various Data in Javascript [duplicate] - javascript

I have been using oneclick methods forever and am rewriting my website and want to switch to a modern of doing things (event listeners)
I understand how to add an event listener.
Const button = document.getElementById('button');
button.addEventListener('click');
I could loop through all the buttons but how would I properly event delegate?
Basically I have a table. And I want to target every button in it that has a specific class “edit-user” and listen for any of those buttons being clicked.
Thanks, but confused at best way to event delegate and target specific elements and having one event listener for entire table. Seems bad to add 50 different listeners for each button.

Event Delegation works on this way:
( more easy with the use of element.matches )
<table id="my-table">
...
<tr>
<td>
<button class="edit-user">aaa</button>
...
<tr>
<td>
<button class="edit-user">bbb</button>
...
<tr>
<td>
<button class="other-class">ccc</button>
...
const myTable = document.querySelector('#my-table')
myTable.onclick = e =>
{
if (!e.target.matches('button.edit-user')) return // reject other buttons
console.log( e.target.textContent) // show "aaa" or "bbb"
// ...
}

Here is an example, note that you can add new buttons dynamically and it still works the way that's not possible when you add an event listener to each element, so in my example there is a button "add more buttons" that adds more buttons dinamically to demonstrate that all are clickable the same way.
var butCont = document.querySelector("#buttons-container");
butCont.onclick = function(e) {
if(e.target.nodeName === "BUTTON") {
//to prevent hiding the snippet with the console
console.clear();
console.log(e.target.textContent);
}
};
for(var i = 0;i < 50; i++) {
butCont.innerHTML += `<button>${i + 1}</button>`;
}
//don't worry the .querySelector will get the first button which is the add more buttons one and not other button
document.querySelector("button").onclick = function() {
butCont.innerHTML += `<button>${i += 1}</button>`;
}
#buttons-container {
width: 300px;
}
button {
width: 30px;
height: 30px;
}
<button style="width: 300px;">add more buttons</button>
<div id="buttons-container">
</div>

Related

Change color of all clicked buttons

I have a set of buttons in an html page of the following form:
<button
id="testID"
mat-mini-fab
ngClass="list-button"
(click)="onClick($event)"
>
Press
</button>
I try to change the color of each button belonging to .list-button class after clicking on it using the following css code:
.list-button:focus {
background-color: #7d698d;
}
However, while the color of the button I click each time changes
(the color of all the previously clicked buttons also changes back to their original color).
How could I fix it? I want all the clicked buttons to remain their new color.
I also tried assigning an id to the buttons of this class and changing their color inside the onClick() method as follows without success. The same problem remains. Could you help me, please?
onclick(event: any) {
const btn = document.getElementById('testID');
if (btn) btn.style.backgroundColor = '#7d698d';
}
ID must be unique
You can use the function and pass el which will refer to this as an argument function onClick(el){......} and use it onClick(this)
No need to get the id you can directly use el.style or event.target.style see the next examples
function onClick(el){
el.style.backgroundColor = '#7d698d';
}
<button
id="testID-1"
mat-mini-fab
ngClass="list-button"
onclick="onClick(this)"
>
Press
</button>
<button
id="testID-2"
mat-mini-fab
ngClass="list-button"
onclick="onClick(this)"
>
Press
</button>
Also You can use the function and pass event which equal event as an argument function onClick(event){......} and use it onClick(event)
function onClick(event){
event.target.style.backgroundColor = '#7d698d';
}
<button
id="testID-1"
mat-mini-fab
ngClass="list-button"
onclick="onClick(event)"
>
Press
</button>
<button
id="testID-2"
mat-mini-fab
ngClass="list-button"
onclick="onClick(event)"
>
Press
</button>
You need to get the id of the button that fired the event
onclick(event: any) {
const btn = document.getElementById(event.target.id);
if (btn) btn.style.backgroundColor = '#7d698d';
}
The reason why the color of the previously clicked buttons is resetting after you click on another one is that the focus is set on the last clicked button. Try using the code in the Venkatesh's answer.
No need for js. Use the focus selector in your stylesheet.
The :focus selector is allowed on elements that accept keyboard events or other user inputs.
~ W3
button.list-button:focus {
background-color: #7d698d;
color: white;
}
<button class="list-button">Click</button>
Edit ~ with js. This method uses .querySelectorAll("button") and toggles the styled class .focus each time the button(s) are clicked. This works well when having multiple buttons.
// Get all the elements on the basis of query selector (button)
let btn = document.querySelectorAll("button");
for (var i = 0; i < btn.length; i++) {
(function(index) {
btn[index].addEventListener("click", function() {
console.log("Clicked Button: " + index);
let isPresent = false;
// Checks if the class is present or not
this.classList.forEach(function(e, i) {
if (e == "focus") {
isPresent = true;
} else {
isPresent = false;
}
});
// Toggles the presence of class (.focus) on the basis of the isPresent variable
if (isPresent) {
this.classList.remove("focus");
} else {
this.classList.add("focus");
}
});
})(i);
}
.focus {
background-color: #7d698d;
color: white;
cursor: pointer;
}
<button id="btn">Click</button>
<button id="btn">Click</button>
<button id="btn">Click</button>

Trying to sum values of inputs created by the user

I have a function that generates the inputs all of class "priceInput" within a table.
And then a function that sums the values of that class.
I've tested the summing equation and I know it works if the "priceInput" class elements are in my html, but not when I have them created by the user. Please help!
function newItem() {
var tab;
tab = document.createElement("div");
tab.innerHTML = `
<table><tbody>
<tr>
<td class="descriptionLabel top priceContainer">Price: $</td>
<td class="descriptionPrice"><input type="number" class="priceInput"></td>
</tr>
</tbody></table>`;
document.getElementById("addtable").appendChild(tab);
}
$(".priceInput").on("keyup", function () {
let sum = 0;
$(".priceInput").each(function () {
sum += $(this).val() / 1;
});
$("#specialTotal").text(sum);
});
<div class="buttonContainer">
<button class="newItemButton" onclick="newItem()">Add New item</button>
</div>
<div id="addtable"></div>
<div class="total" id="specialTotal"></div>
I am guessing this is happening because of a common problem. What I believe is happening is that when $(".priceInput").on("keyup",... is run, it attaches the event listener to the elements with .priceInput that exist at the time of execution of this command.
As a result, any dynamically created elements with .priceInput do not have an event listener attached to them. There are two ways to fix this:
Attach the event listener each time to any new element you create.
Attach the listener to the whole document and verify if the event.target is the element that you wish it to be. If it is an element with .priceTable then do your summing.
Fiddle applying method 1: https://jsfiddle.net/a8b9whez/
Fiddle applying method 2: https://jsfiddle.net/t8e0obyk/3/

Click and toggle between two classes on an array (no JQuery)

I need to make a site where I can add something to a shoppingcart. I do not need to store any data, so just changing a class to 'addedInCart' is enough. This is what I have so far, but it's not working yet. I know all the classnames I got are coming back in an array. I just dont know how to change them if the button is clicked. I tried a lot with the addEventListener and the toggle, but I just started coding, not everything is clear for me yet. I am not alloud to use Jquery, only HTML and Javascript.
This is what I have in Javascript:
var buyMe = document.getElementsByClassName("materials-icon");
function shoppingcart() {
for(let i = 0; i < buyMe.length; i++){
buyMe[i].classList.toggle("addedInCart");
buyMe[i].addEventListener("click", toggleClass, false)
}
}
This is what my button looks like:
<button class="material-icons" onclick="shoppingcart()"></button>
Thank you for your time!
Use event delegation to be able to use one handler. Use a data-attribute to verify the button as being a button for adding to cart. Something like:
document.addEventListener("click", handle);
function handle(evt) {
const origin = evt.target;
if (origin.dataset.cartToggle) {
// ^ if element has attribute data-cart-toggle...
origin.classList.toggle("addedInCart");
// ^ ... toggle the class
}
};
.addedInCart {
color: red;
}
.addedInCart:after {
content: " (in cart, click to remove)";
}
<button class="material-icons" data-cart-toggle="1">buy</button>
<button class="material-icons" data-cart-toggle="1">buy</button>
<button class="material-icons" data-cart-toggle="1">buy</button>

Adding onclick event to dynamically created buttons in javascript without using library

I want to add onclick event to dynamically created buttons based on data. I tried to add the event in the same loop as the buttons are created. but only the last button onclick event works and the rest doesnt work.
if i add a separate loop to add the events, it works for all buttons as expected. I want to know why I couldn't achieve it in the same loop.
the commented block is the code i tried to add inside the loop for adding event.
Also, please suggest if there is any better way to work this(just want to try without libraries, no particular reason).
Code:
function a() {
var objContent = document.getElementById('content')
var fruits = ['apple', 'banana', 'orange', 'grapes']
for (var i in fruits) {
var buildHtml = `<div id="content">
<div id="editsection">
<button id="${fruits[i]}" class="buttons" value="${fruits[i]}">${fruits[i]}</button>
</div>
<div id="deletesection">
</div>
</div>`
objContent.innerHTML += buildHtml
**/* var objFruitButton = document.getElementById(fruits[i])
objFruitButton.addEventListener("click", function (){
onClick(objFruitButton.value)
}, false); */**
}
fruits.forEach(function(i) {
var objFruitButton = document.getElementById(i)
objFruitButton.addEventListener("click", function() {
onClick(objFruitButton.value)
})
})
}
function onClick(x) {
console.log("value:", x);
}
a();
<div id="content"></div>

Using checkboxes to update UI in realtime

I'm currently in the process of trying to develop a smarter UI for one of my clients. However the only code I can use to develop this 'feature', is pure JS. I have no access to the source HTML or CSS files the only access I have is the ability to inject JavaScript through an external .js file.
I'm not too familiar with JS, but I can work my way around a basic script or two.
Scenario
What we're doing is allowing users to edit PDF Templates online using a software called Core Create. The UI accessed through the browser is quite cluttered and I would like to provide an option to hide and show UI elements <textareas>/<inputs> through the use of checkboxes.
Here is a very basic JS Fiddle that I have built with the
intention of hiding and displaying UI.
The page in question
Above is a screen grab of the page I am working with, on the left you can see the UI and its composition on the right within the 'Inspect Element' tool.
I have come to the conclusion that I need to iterate through the highlighted selection and link them accordingly with seven checkboxes. The result would then be a selection of checkboxes that would hide / display the correct UI element.
The Caveat
In realizing I cannot edit or introduce new HTML I noticed the lack of on-click attributes. So I'm a bit lost on how to invoke the JavaScript I will eventually build.
My Question
With my limited knowledge of JS I don't know how I would iterate though div elements editoraccvar - editoraccvar6 picking out the ones I need to manipulate.
Due to the lack of ID's / Names (I assume it would have to be done using Parent/Child rules somehow, as the classes are widley used by the rest of the UI. I would appreciate a small example demonstrating how I could achieve this, so I can learn from it.
I should clarify, I have already added the checkboxes to the page, I just need to build the JS link between the Checkbox and the UI element I'm attempting to target. You can find all attributes linking to these checkboxes included in the JS Fiddle.
EDIT // A Working Simplified Example;
Due to some confusion I have 'frankensteined' some code together to show the final result I am after. A working example of sorts. The actual result needs to target 7 Checkboxes and 7 Divisions. I'll list thier common properties below.
// This script is already in place and constructed by the system.
// Written inside script tags and located straight after 'editopt1'.
// $(document).ready(function() {
// $('#checkboxopt1').click(function() {
// if ($('#checkboxopt1').val() == 'true') {
// $('#opt1').val('false');
// $('#checkboxopt1').val('false');
// $('#checkboxopt1').prop('checked', false);
// $('#previewrefresh').trigger('click');
// } else {
// $('#opt1').val('true');
// $('#checkboxopt1').val('true');
// $('#checkboxopt1').prop('checked', true);
// $('#previewrefresh').trigger('click');
// };
// });
// });
function exFunction() {
// Check the function is called
console.log("200 : OK");
// grab all elements with the class, .field-summernote
var uiblocks = document.querySelectorAll('.field-summernote');
for (var i = 0; i < uiblocks.length; i++) {
var current = uiblocks[i];
if (current.className.indexOf('editoraccvar') < 0) //not found: -1
return;
// check elements in the array
console.log(current);
// control the elemets in the array.
if (document.getElementById('checkboxopt1').checked) {
uiblocks[0].style.display = 'block'; // display the element
} else {
uiblocks[0].style.display = 'none'; // hide the element
}
}
};
// Trigger the collection the check, and the control.
var x = document.getElementById("checkboxopt1");
x.addEventListener("click", function() {
console.log("Opt");
exFunction();
});
.editoraccvar1 {
width: 300px;
background: #0ff;
padding: .5em;
}
.editoropt1 {
width: 300px;
background: #ff0;
padding: .5em;
}
textarea {
display: block;
width: 95%;
resize: none;
padding: .5em;
}
<!-- I'm trying to hide & show this entire division... -->
<div class="seq-box-form-field field-summernote editoraccvar1 ">
<label for="accvar1">Ground Floor Info</label>
<div class="clearfix"></div>
<textarea id="richaccvar1" name="richaccvar1" class="summernote"></textarea>
<input type="hidden" name="accvar1" id="accvar1" value="" />
</div>
<!-- Using only what the system has supplied. -->
<div class="seq-box-form-field editoropt1 ">
<label for="opt1"><span style="padding-right: 10px; vertical-align: 1px;">Ground Floor </span>
<input type="checkbox" name="checkboxopt1" id="checkboxopt1" value="true" checked="true" />
<input type="hidden" name="opt1" id="opt1" value="true" />
</label>
</div>
Divisions <div class=""></div>
* editoraccvar,
editoraccvar1,
editoraccvar2,
editoraccvar3,
editoraccvar4,
editoraccvar5,
editoraccvar6*
Checkboxes <input id=""></input>
* checkboxopt,
checkboxopt1,
checkboxopt2,
checkboxopt3,
checkboxopt4,
checkboxopt5,
checkboxopt6,*
As far as I can see, your problem boils down to link checkboxes (that seem to have been generated in some way) to "division" parts of your html that you want to hide. Plus, you have to inject javascript code in the page (so I guess the less code the better).
One approach could be as follows:
// Wrap the code in an anonymus function, to avoid clustering the global space.
(function (domElements) {
// This is the callback that will fire when a checkbox is clicked.
function clickCallback() {
// the context of this callback is the DOM element thus we can access its attributes through this.
// extract the checkNumber of the class of the element. This number is the link to the division that we want to hide/show.
var checkNumber = ((/ editoropt(\d*) /).exec(this.className))[1],
checkBox = document.getElementById('checkboxopt' + checkNumber),
division = document.querySelectorAll('.editoraccvar' + checkNumber)[0];
// Hide/show division, update checkBox state.
toggleElements(division, checkBox, window.getComputedStyle(division).display === 'none');
}
function toggleElements(division, checkBox, isShown) {
// Toggle the division (show/hide) accordingly.
division.style.display = isShown ? 'block' : 'none';
// Due to the fact that the event listener is attached to the parent of the checkBox, we need to maintain consistency manually.
checkBox.checked = isShown;
}
// Remove from the array of DOMElements those that aren't checkboxes and add a click event listener to each of them.
domElements
.filter(function (el) {
return el.className.indexOf('editoropt') !== -1;
})
.forEach(function (el) {
el.addEventListener('click', clickCallback, false);
});
// Call the function passing the dom elements with class '.seq-box-form-field' as argument. Checkboxes are contained within them. Also, transform the nodelist
// into a proper array so that methods defined in Array.prototype can be used.
})([].slice.call(document.querySelectorAll('.seq-box-form-field')));
The code is commented and (I think) quite self-explanatory. However, if you have any doubt or want me to elaborate any point further, please, let me know.
Finally, here's the working fiddle.
UPDATE
Same function (more or less) but now it accepts an array of values that will correspond to the initial state of the checkboxes:
(function (domElements, cbState) {
function clickCallback() {
toggleElements(this.className);
}
function toggleElements(className, initialShow) {
var checkNumber = ((/ editoropt(\d*) /).exec(className))[1],
checkBox = document.getElementById('checkboxopt' + checkNumber),
division = document.querySelectorAll('.editoraccvar' + checkNumber)[0],
isShown = initialShow === undefined ? window.getComputedStyle(division).display === 'none' : initialShow;
division.style.display = isShown ? 'block' : 'none';
checkBox.checked = isShown;
}
domElements
.filter(function (el) {
return el.className.indexOf('editoropt') !== -1;
})
.forEach(function (el, index) {
el.addEventListener('click', clickCallback, false);
toggleElements(el.className, cbState[index]);
});
// Initial state of the checkboxes goes in the second parameter. The index in the array correspond to the checkbox position in the page.
})([].slice.call(document.querySelectorAll('.seq-box-form-field')), [false, false]);
Here's the Fiddle to play with. Hope it helps.
The other half of your problem, not addressed in the other answer has to do with events. Generally, adding an "onclick" attribute to the actual HTML is considered bad practice. You can attach event handlers with Javascript.
var a = document.getElementById("checkboxopt1");
a.addEventListener("click", exFunction, false);
See the manual for more info about how to use this.
Looks like that you need the elements that have the class "field-summernote", but not the class "editorbdyvar".
You can use a query selector to get elements by class name using the default tools from Javascript:
var items = document.querySelectorAll('.field-summernote');
for(var i = 0; i<items.length; i++){
var current = items[i];
if( current.className.indexOf('editoraccvar') < 0) //not found: -1
return;
//now you can manipulate the current element
console.log(current);
}
well ... you should either learn javascript, DOM, HTML and CSS or hire an somebody that can do it.
in my opinion the latter would come cheaper.
if not,
here goes something to put in your script.js file.
the checkboxes must have the id="toggleTextareas" respectively id="toggleInputs".
(function isolateScope() {
tryInit();
function tryInit() {
if(document.readyState!="complete"){
setTimeout(tryInit, 100);
}else{
createUI();
init();
}
}
function createUI(){
var div=document.createElement("div");
div.className="addon-floating-toolbar"
div.style.position="fixed";
div.style.zIndex="999999";
div.style.background="#EEE";
div.style.padding="5px";
div.innerHTML='<input type="checkbox" id="toggleTextareas">toggle Textareas<br>'
+'<input type="checkbox" id="toggleInputs">toggle Inputs';
document.body.appendChild(div);
}
function init() {
var tta=document.getElementById("toggleTextareas");
var ti=document.getElementById("toggleInputs");
var textareaVisible=true;
var inputVisible=true;
tta.onclick=toggleTextareas;
ti.onclick=toggleInputs;
function toggleTextareas() {
var elms=document.querySelectorAll("textarea");
textareaVisible=!textareaVisible;
if (textareaVisible) {
show(elms);
}else{
hide(elms);
}
}
function toggleInputs() {
var elms=document.querySelectorAll("input");
inputVisible=!inputVisible;
if (inputVisible) {
show(elms);
}else{
hide(elms);
}
}
function show(collection) {
for (var i = 0; i < collection.length; i++) {
collection[i].style.display="";
}
}
function hide(collection) {
for (var i = 0; i < collection.length; i++) {
collection[i].style.display="none";
}
}
}
})();
let me know if it works,
cheers.
You can traverse all your fields and generate a checkbox that will toggle it open/close for each of your fields. Also set the checkbox label as innerText of the corresponding field.
// Block to be run
generateCheckboxes = function() {
var button = document.getElementById("generateButton");
button.parentNode.removeChild(button);
// grab all elements with the class, .field-summernote
var uiblocks = [].slice.call(document.querySelectorAll('.field-summernote')).filter(function(x) {
return x.className.indexOf('editoraccvar') >= 0
});
if (!uiblocks.length) return;
var chcontainer = document.createElement('div');
chcontainer.style.display = "inline-block";
document.body.insertBefore(chcontainer, document.body.children[0]);
uiblocks.forEach(function(x) {
var cdiv = document.createElement('div');
var clabel = document.createElement('label');
clabel.innerHTML = x.innerText.trim();
var cinput = document.createElement('input');
cinput.type = 'checkbox';
cinput.checked = true;
cinput.onchange = function(ev) {
var checked = this.checked;
x.style.display = checked ? "" : "none";
}
cdiv.appendChild(clabel);
cdiv.appendChild(cinput);
cdiv.appendChild(document.createElement('br'));
chcontainer.appendChild(cdiv);
})
};
#container {
width: 150px;
}
input {
float: left;
}
label {
width: 120px;
display: block;
float: right;
text-align: left;
}
<button onclick="generateCheckboxes()" id="generateButton">Generate Checkboxes</button>
<div id="example" class="field-summernote editoraccvar">
<br/>
<br/>
<span>Zero</span>
<br/>
<textarea></textarea>
</div>
<div id="example1" class="field-summernote editoraccvar1">
<br/>
<br/>
<span>One</span>
<br/>
<textarea></textarea>
</div>
<div id="example2" class="field-summernote">
<br/>
<br/>
<span>Two</span>
<br/>
<textarea></textarea>
</div>
Fiddle

Categories

Resources