I'm trying to toggle the text in a button every time it's clicked, between "READ" and "NOT READ". The buttons have been created dynamically with js and placed into an HTML table. Each button has a unique ID, but the same class name.
I've written an if statement that works for the first button that is set in the table, but the same if statement wont work for the buttons created dynamically.
I've tried lots of different variations for the if statements. I'm not sure if the best way would be to access the unique id's, but I don't know how to do that.
Any help is appreciated! Thanks
Here's a repl https://repl.it/repls/SpryVisibleMining
function toggleText(){
if (readButton.innerHTML == "READ"){
readButton.innerHTML = "NOT READ";
} else if (readButton.innerHTML == "NOT READ"){
readButton.innerHTML = "READ";
} else {
null
}
}
And this is if statement that wont wont do anything
function toggleOthers() {
let toggle = document.getElementsByClassName(".readBtn")
toggle[0].addEventListener("click", () => {
if (toggle.innerHTML == "READ") {
toggle.innerHTML = "NOT READ"
} else if (toggle.innerHTML == "NOT READ") {
toggle.innerHTML = "READ"
} else {
null
}
})
}
toggleOthers()
The problem lies in how you are listening for the click events. Your toggleText function is triggered whenever you click the #readed button with the onclick attribute. But inside the toggleText function you add another event listener to the same button, adding a new event listener every time you click the button.
So every time you click the button you increment the amount of times you are calling toggleText.
Remove the onclick from the button and change the id to a class attribute. You said you would have multiple buttons, so having multiple buttons with the same id won't do it.
<button class="readed">READ</button>
Because you want to listen for the click event on dynamically created elements I suggest you use Event Delegation. This means listening for the click event on a parent element, this could be your table#shelf element, and check which element has been clicked. If A has been clicked, then do X, if B has been clicked, then do Y.
Listen for click event on your table element.
var table = document.getElementById('shelf');
table.addEventListener("click", tableClickHandler);
In tableClickHandler check which element has been clicked. You can do it by getting the clicked target and use the closest method to see if it really is the element you want to be clicked.
For example when you would have a <span> in your <button>, event.target would be <span>. But you want the <button> element. closest goes up in the DOM tree to see if it finally reaches an element that is the <button> you want and returns it.
You can do this for any button inside of your table.
function tableClickHandler(event) {
var readed = event.target.closest('.readed');
if (readed) {
toggleText(readed);
}
}
Modify your toggleText function so that it can take any <button> you throw add it that you want the text toggled in. Add a button parameter which represents the current button.
// Toggle text when clicked.
function toggleText(button) {
if (button.innerHTML == "READ") {
button.innerHTML = "NOT READ";
} else if (button.innerHTML == "NOT READ") {
button.innerHTML = "READ";
} else {
null
}
}
For example you can use this:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button onclick="toggle(this)">not read</button>
<script>
function toggle(e) {
let txt = e.innerText;
e.innerText = txt == 'not read' ? 'read' : 'not read';
}
</script>
</body>
</html>
Let me know if it's not suitable for your use case ...
And also you can use querySelectorAll() to get all buttons and then set this event with a for() loop.
Try
toggleText = b=> b.innerHTML = b.innerHTML=='READ'?'NOT READ':'READ'
<button id="A" onclick="toggleText(this)">NOT READ</button>
<button id="B" onclick="toggleText(this)">NOT READ</button>
According to your repl, you also registered an onclick-Handler on your button, too.
So calling toggleText() while clicking the "Read/Not-Read"-button, will effectively register another onclick-Handler. This will repeat as often as you press the button.
This will run as follows:
run onclick-Handler toggleText(); this will register another EventListener
run the EventListener (registered first by index.js)
run the new registered EventListener (registered by button.onclick)
rinse and repeat ...
Just remove it in your HTML:
<td><button id="readed">READ</button></td>
You have to init an array named books, and each time you add a new book, you have to push the new book to the books array.
And also you have to set a flag as hasReadBook to the Book class.
When you are going to render your table row you have to write an if, for making the flag to string in dom.
function updateTable() {
//anythings needs to done for updating table.
//for hasReadBook flag you should do like this:
const hasReadBookString = books[i].hasReadBook ? "Read" : "Not Read";
}
And you need to make a loop on readBtn HTML collection, to know which index is going to change:
let books = [{...}];
let toggles = document.getElementsByClassName(".readBtn");
for (var i = 0; i < toggles.length; i++){
labels[i].addEventListener('click', function(e) {
books[i].hasReadBook = !books[i].hasReadBook;
})
}
updateTable();
Related
I'm making a battleship game in Javascript and I have a problem with a function that changing button text content. I want to do that when the user click the button the text content of the button changes.
function changePosition(eventBtn){
if(eventBtn.target.textContent=='perpendicularly'){
eventBtn.target.textContent='horizontally';
}
else{
eventBtn.target.textContent='perpendicularly';
}}
But when I click on the button nothing changes. I think the problem is with the else statement because when I delete this statement all work.
Make sure that you are adding the listener to the button appropriately.
function changePosition(event) {
if (event.target.textContent === 'Perpendicularly') {
event.target.textContent = 'Horizontally';
} else {
event.target.textContent = 'Perpendicularly';
}
}
document.querySelector('.direction').addEventListener('click', changePosition);
<button class="direction">Perpendicularly</button>
In regards to the following statement:
But when I click on the button nothing changes. I think the problem is with the else statement because when I delete this statement all work.
Your event may be firing twice. It could be calling your event listener twice, effectively reverting the change it just made to the text.
Suggested improvement
Magic strings are bad, and I would recommend the use of enumerable values instead.
const Direction = {
PERPENDICULARLY: 'Perpendicularly',
HORIZONTALLY: 'Horizontally'
};
const changePosition = event => {
event.target.textContent =
event.target.textContent === Direction.PERPENDICULARLY
? Direction.HORIZONTALLY
: Direction.PERPENDICULARLY
};
document.querySelector('.direction').addEventListener('click', changePosition);
<button class="direction">Perpendicularly</button>
I am trying to make a simple Shopping List App in which user can Add, Delete and mark the task done when completed. So far, I am able to add the task but facing problem in executing the done and delete functions. I am getting an error because when I execute it, the done and delete buttons are not there but what should I do to fix it?
var inp = document.getElementById("form");
var button = document.getElementById("click");
//Create List Function with Done and Delete Buttons
function addVal() {
var ul = document.getElementById("list");
var li = document.createElement("li");
var span = document.createElement("span");
var done = document.createElement("button");
var del = document.createElement("button");
li.appendChild(document.createTextNode(""));
span.appendChild(document.createTextNode(inp.value));
done.appendChild(document.createTextNode("Done"));
del.appendChild(document.createTextNode("Delete"));
li.appendChild(span);
li.appendChild(done);
li.appendChild(del);
done.setAttribute("class", "doneBut");
del.setAttribute("class", "delBut");
ul.appendChild(li);
inp.value = "";
}
//Get Input Length
function checkLength() {
return inp.value.length;
}
//Run function on Button Click
function onButtonClick() {
if (checkLength() > 0) {
addVal();
}
}
//Run function on Enter Keypress
function onEnter(event) {
if (checkLength() > 0 && event.which === 13) {
addVal();
}
}
//Trigger Events
button.addEventListener("click", onButtonClick);
inp.addEventListener("keypress", onEnter);
//Done and Delete Button Functions
var doneButton = document.getElementsByClassName("doneBut");
var deleteButton = document.getElementsByClassName("delBut");
function doneTask() {
doneButton.parentNode.classList.add("done");
}
function delTask() {
deleteButton.parentNode.classList.add("delete");
}
doneButton.addEventListener("click", doneTask);
deleteButton.addEventListener("click", delTask);
<input type="text" placeholder="Enter Your Task..." id="form" />
<button id="click">Add Task</button>
<h2>List:</h2>
<ul id="list"></ul>
Please Help.
Your problem is that the code tries to add events before the buttons exist. The buttons don’t exist until the addVal function gets called. Since addVal is not being called before the you try to add your event handlers, the getElementById returns null, and you attempt to add an event listener to null.
Additionally it looks like you’re planning to add multiple done and delete buttons. That wouldn’t normally be a problem, except you’re referencing them by ID, and IDs MUST be unique. You’ll need to switch this to a class or an attribute, since you’ll need one per item in the shopping cart.
You’ll probably want to look into event delegation, so that you can add your events once to the page before any buttons exist. https://javascript.info/event-delegation
It's most likely because your script is running before your code is running. Add the <script> tags just before the closing </body> tag to fix it:
<script>/* Your code here */</script>
</body>
You need to place this in a window.onload function, or run it in a function inside of the body tag's onload. Those elements don't exist yet when the script is run:
window.onload = function() {
var inp = document.getElementById("form");
var button = document.getElementById("click");
button.addEventListener("click", onButtonClick);
inp.addEventListener("keypress", onEnter);
}
I have a list of controls contained in a parent div called overlay-controls.
There is many list controls that each have their own overlay-controls.
I am using a for loop to add the event listener to each button that contains the class delete.
Before the user can delete the item, they must confirm. I am trying to attach this to every delete button found in overlay-controls.
I got it to work using a for loop but I know there is a better way using bubbling and capturing. I am having trouble targeting only the delete class inside overlay-controls by bubbling up to parent div.
See the live demo here by clicking on each delete button: http://jsfiddle.net/8qqfeoa2/1/
Here is my code using the for loop:
(function() {
function getConfirmation(e){
var retVal = confirm("Are you sure you want to delete this request?");
if( retVal == true ){
return true;
}else{
e.preventDefault();
return false;
}
}
var del = document.querySelectorAll('.delete');
for(var i = 0, len = del.length; i < len; i++){
del[i].addEventListener('click', function(e) {
getConfirmation(e);
}, false);
}
}());
You dont event need the For / .each loop
Jquery takes care of it internally
$('.delete').on('click', function(e){
getConfirmation(e);
});
Provided you are using jQuery and in getConfirmation method you may also get that specific (clicked) element by using e.target which returns the target on which click happened.
Only Javascript solution
As you requested one
var deletebuttons = document.getElementsByClassName('delete');
for(var button in deletebuttons) {
button.onclick = getConfirmation;
}
How can I figure out what button was clicked on last? For example I have:
<input type="button" name= "zoomer" value="State View" id= 'States View' onclick="zoomout()"/>
<input type="button" name= "zoomer" value="County View" id= 'Counties View' onclick="countyView()"/>
But whenever I change a RADIO button, I want it to take into account which button was clicked last (County View or State View). Is it possible to do this?
You could keep a global JavaScript variable var last_clicked which is updated in the functions zoomout() and countyView(), and then check the value of last_clicked when you change the radio button. Alternatively, you can terminate the calls to the functions within the onclick event with a semicolon, then assign the value to last_clicked inside the onclick event string (although I wouldn't recommend it as it can make your code messy).
var lastClicked = "none";
function zoomout()
{
// your code
lastClicked = "states";
}
function countyView()
{
//your code
lastClicked = "county";
}
if(lastClicked == "county")
{
}
else if(lastClicked == "states")
{
}
it's possible by using an external variable such as
var clickedLast = "";
function zoomout() {
clickedLast = "stateview";
... your code ...
}
function countyView() {
clickedLast = "countyview";
... your code ...
}
I'll keep this short - I've got a list of buttons, that I create using a loop, and when one of them gets clicked I want to be able to pass its id attribute to another file in order to dynamically generate a new page.
Here's the code:
for (var i in data.contacts) {
var temp = document.createElement('div');
temp.className = "contacts";
var dude = document.createElement('input');
dude.type = "button";
dude.value = data.contacts[i];
dude.id = data.contacts[i];
dude.className = "dude_button" + data.contacts[i];
dude.addEventListener('click', function(event) { gotoProfile(dude.id); }, false);
temp.appendChild(dude);
temp.appendChild(document.createElement('br'));
theDiv.appendChild(temp);
}
// and now in another file, there's gotoProfile():
function gotoProfile(x) {
var username = document.getElementById(x).value;
if (xmlHttp) {
try {
.... etc.
Now see this works, sort of, but the problem is that when I click any button, it only passes the last dude.id value from the list data.contacts. Obviously I want every button's addEventListener to pass its own data.contacts[i] value, instead of just the last one.
Help appreciated, thanks guys.
Because JavaScript has no block scope, dude will refer to the last assigned element (because the loop finished) when the event handler is called. You have to capture the reference to the current dude by e.g. using an immediate function:
dude.addEventListener('click', (function(d) {
return function(event) {
gotoProfile(d.id);
}
}(dude)), false);
This is a common error when creating functions in a loop.
But you can make it even easier. The event object has a property target that points to the element the event was raised on. So you can just do:
dude.addEventListener('click', function(event) {
gotoProfile(event.target.id);
}, false);
And with that said, you don't need to add a handler for every button. As you are doing the same for every button, you could attach the same event handler above to the parent of the buttons (or a common ancestor) and it would still work. You just have to filter out the clicks that don't happen on a button:
parent.addEventListener('click', function(event) {
if(event.target.nodeName == 'INPUT' && event.target.type == "button") {
gotoProfile(event.target.id);
}
}, false);