This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 2 years ago.
I am trying to make a website, in which one makes a questions and there will be options for that questions.
I have a add option button, when user clicks the add option button, new option is added by this function.
function addOption(elem) {
elem = $(elem);
let ul = $(elem.parent().children()[1]);
let addedElem = $(
`<li class='ques-li'><input type='text' name='ques-option' class='ques-option' value='Option'><p class='remove-op'>X</p></li>`
);
addedElem.appendTo(ul);
}
let addOp = $('.ques-add');
$('.ques-add').click(function () {
addOption($(this));
});
This works well with one question, but when a user adds another question.
It is by this function.
let addQuestion = $('form button');
addQuestion.click(function () {
let quesBox = $('div.questions-box');
quesBox.html(quesBox.html() + `<div class='ques'>
<input type="text" name="ques-name" class="ques-name">
<ol class='ques-ul'>
<li class='ques-li'><input type="text" name='ques-option' class='ques-option'>
<p class='remove-op'>X</p>
</li>
<li class='ques-li'><input type="text" name='ques-option' class='ques-option'>
<p class='remove-op'>X</p>
</li>
</ol>
<p class='ques-add'>Add Another</p>
</div>`)
});
When i click the add Question button, it also works
but when i click the add option button on a newly created question,
it does not create a new option.
I noticed when i create a new question, it does not get the new option button.
So, i updated my code as follows :-
let addOp = $('.ques-add');
$(document).bind('DOMNodeInserted', function(){
addOp = $('.ques-add');
});
addOp.click(function () {
addOption($(this));
});
It also does not work.
I have no idea why this code is not working.
Thanks in advance.
Try using delegation approach that uses .on(). This will assure that the event will be attached to all the elements that are added to the document at a later time:
$('body').on('click', '.ques-add', function () {
addOption($(this));
});
Related
Basically I'm trying to make a todo list app similar to Trello. I have a button that when pressed turns into an input element, gets a "To Do Task" item and adds that to a list. This is achieved by this piece of code:
function createCardBoxNode(title){
/*HTML looks like:
<div class="task-card">
<div class="writings">
<p class="title">Tasks To Do</p>
<ul id="tasks">
<li>Task 1</li>
</ul>
</div>
<button class="btn-add-task">
Add New Task...
</button>
<input....>
</div>
*/
var containerBox = createElement('div', {class:'task-card'});
var writingPartBox = createWritingAreaNode(title);
var newTaskBtn = createElement('button', {class:'btn-add-task show'},'Add New Task...');
var newTaskInput = createElement('input', {class:'new-task hide', type:'text', placeholder:'New Task'});
//When 'add new task' is clicked, make it an input area
newTaskBtn.addEventListener('click', function(){
newTaskBtn.classList.remove('show');
newTaskBtn.classList.add('hide');
newTaskInput.classList.remove('hide');
newTaskInput.classList.add('show');
newTaskInput.focus();
});
// when input is entered, that's a new "To Do Task" so add it to the list
newTaskInput.addEventListener('keyup', function(e){
if (e.keyCode === 13){
// If Enter is pressed
var newTask = createListItems(newTaskInput.value);
var listArea = document.getElementById('tasks');
listArea.appendChild(newTask);
newTaskInput.classList.remove('show');
newTaskInput.classList.add('hide');
newTaskInput.value = '';
newTaskBtn.classList.remove('hide');
newTaskBtn.classList.add('show');
}
});
containerBox.appendChild(writingPartBox);
containerBox.appendChild(newTaskBtn);
containerBox.appendChild(newTaskInput);
return containerBox;
}
This works fine until I add another Card at the same time and decide to add new tasks to the second card. Then every task gets added to the first card. I wonder if there is any way to check if the "input" that's being sent is going to a specific card checking the card's title. I don't have any limits on how many tasks can be added to each card, and don't want to add that. I also want the user to be able to work on two separate cards at the same time. As a beginner, I also want to fix this using only JavaScript. I hope I've explained the issue well enough.
Edit:
I have tried doing this:
if (document.querySelector('.title').innerText === title){
var newTask = createListItems(newTaskInput.value);
var listArea = document.getElementById('tasks');
listArea.appendChild(newTask);
newTaskInput.classList.remove('show');
newTaskInput.classList.add('hide');
newTaskInput.value = '';
newTaskBtn.classList.remove('hide');
newTaskBtn.classList.add('show');
But then I cannot add anything new to the second box.
I think your main problem is that you use the <ul> with the same id for different cards.
First of all, change your markup and replace <ul id="tasks"> with <ul class="tasks-list">
<div class="task-card">
<div class="writings">
<p class="title">Tasks To Do</p>
<ul class="tasks-list"> <!-- !!! here !!! -->
<li>Task 1</li>
</ul>
</div>
<button class="btn-add-task">
Add New Task...
</button>
<input....>
</div>
and then change that selector in your keyup handler:
//...
var listArea = containerBox.querySelector('.tasks-list');
// ...
Also, it would be better to declare
var listArea = containerBox.querySelector('.tasks-list');;
outside your handler.
I solved it on my own.
The trick is to not select anything at all. Everytime you queryselect anything, or get an element using it's ID - it will select the first element with that id or class.
What I ended up doing is to combine two of my functions, have a function generate my and then just straight up append the list items into that ul. No selection whatsoever.
So I am making some overrides on a Wordpress plugin. I need to copy the event listener on an element and then replace the element and add it back. The event listener is generated by the plugin.
I thought getEventListeners() would work but I have read that it only works in console. If that is this case I'm really astounded. We're in freaking 2020 and I am not finding an obvious solution to this.
What is the solution here people?
Below is the code I was trying to implement having assumed getEventListeners wasn't just a console function.
// Edit Affirm
(function replaceAffirm() {
if (document.querySelector(".affirm-modal-trigger")) {
const learnMore = document.querySelector("#learn-more");
const modalTrigger = document.querySelector(".affirm-modal-trigger");
const clickHandler = getEventListeners(modalTrigger).click[0].listener;
const substr = learnMore.innerHTML
.toString()
.substring(
learnMore.innerHTML.indexOf("h") + 2,
learnMore.innerHTML.length
);
learnMore.innerHTML = "Easy Financing with " + substr;
modalTrigger.addEventListener("click", clickHandler);
} else {
setTimeout(function () {
replaceAffirm();
}, 250);
}
})();
HTML
<p id="learn-more" class="affirm-as-low-as" data-amount="20000" data-affirm-color="white" data-learnmore-show="true" data-page-type="product">
Starting at
<span class="affirm-ala-price">$68</span>
/mo with
<span class="__affirm-logo __affirm-logo-white __ligature__affirm_full_logo__ __processed">Affirm</span>.
<a class="affirm-modal-trigger" aria-label="Prequalify Now (opens in modal)" href="javascript:void(0)">Prequalify now</a>
</p>
You can't copy event listeners, but it seems because of the structure of your HTML it's more likely that you shouldn't need to re-add it. Instead of editing the HTML and removing the event listener by doing so, the best bet would be to edit around it.
If you want to remove the text nodes you can iterate through childNodes and separate out what should be removed.
Then to rebuild the appropriate text where you want it you can use insertAdjacentText
if (document.querySelector(".affirm-modal-trigger")) {
const learnMore = document.querySelector("#learn-more");
const modalTrigger = document.querySelector(".affirm-modal-trigger");
const children = Array.from(learnMore.childNodes);
children.forEach(child => {
if (child.nodeType === Node.TEXT_NODE || child.matches(".affirm-ala-price")) {
if (learnMore.contains(child)) {
learnMore.removeChild(child);
}
}
});
learnMore.insertAdjacentText("afterBegin", "Easy Financing With ");
modalTrigger.insertAdjacentText("beforeBegin", " ");
} else {
setTimeout(function() {
replaceAffirm();
}, 250);
}
<p id="learn-more" class="affirm-as-low-as" data-amount="20000" data-affirm-color="white" data-learnmore-show="true" data-page-type="product">
Starting at
<span class="affirm-ala-price">$68</span> /mo with
<span class="__affirm-logo __affirm-logo-white __ligature__affirm_full_logo__ __processed">Affirm</span>.
<a class="affirm-modal-trigger" aria-label="Prequalify Now (opens in modal)" href="javascript:void(0)">Prequalify now</a>
</p>
Yes waiting for the Html element to be loaded and checking until it gets loaded is okay and this is one of the correct ways to wait for it.
As per my understanding of your issue, you just have to change the text of the learn-more element.
for that, it is not necessary to copy event listener and then again binding it.
Instead of replacing the whole element just change the text keeping the same element.
So it gets binded with the event listener by default.
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 5 years ago.
Below is my html and javascript. The Js is located in a app.js file and the html is a yes or no question. At the bottom of the javascript I have code that should fire on click events. Right now it is just to see if it detects a click on the p elements in the html but hasn't worked so far.
Can anyone detect why this event isnt taking place?
The other jQuery int he code does work but this jQuery isnt invoked by a function and is invoked based of strictly using a selector which leads me to believe there is something wrong with how I used document.ready in my project.
Html:
<div class="question animated slideInUp">
<h3>Do you know your individual monthly rent cost?</h3>
<div class="rentanswer">
<p>Yes</p><p>No</p>
</div>
<!--
<p>If not, we'll get the average rent of a zip code for you from Zillow</p>
<form onsubmit="Obj(this.p.name,this.p.value)">
<input type="number" id="p" name="rent" placeholder="Leave blank if N/A" >
<input id="s" type="submit" value="Submit">
</form>
-->
</div>
Javascript:
var quest = ['../partials/income.hbs', '../partials/state.hbs', '../partials/rent.hbs', '../partials/zip.hbs', '../partials/roomate.hbs', "../partials/summary.hbs"];
var iterator = 0;
//creates object that gets sent to api
function Obj(name, vale){
event.preventDefault(); //prevents pg refresh
$('.form').load(quest[iterator], function () {
$('.bar p#' + name).css("border","2px solid black").addClass('enter').next().css({"border":"2px solid black","pointer-events":"auto","cursor":"pointer"});
});
//loads next html question to page
if(name){
data[name] = vale;
}
//move array accessor up to next question
iterator++;
console.log(data);
}
var data = {};
//user can click already submitted values to load that orignal question and change it/
function redo(q, id) {
q = Number(q);
//if the current queston on the page is not the one being clicked we then change it to the one being clicked
if (q + 1 != iterator) {
$('.form').load(quest[q], function () {
$('#p').val(data[id]);
});
iterator = q + 1;
};
};
$(document).ready(function(){
$('.rentanswer p').on('click',function(){
console.log('hey');
});
});
Here you go with a solution https://jsfiddle.net/g8xt5g4y/
$(document).ready(function(){
$(document).on('click','.rentanswer p',function(){
console.log($(this).text());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>Do you know your individual monthly rent cost?</h3>
<div class="rentanswer">
<p>Yes</p>
<p>No</p>
</div>
I believe your HTML is getting rendered dynamically, that's why your click method isn't working.
Try event delegate (mentioned in th above code).
Hope this will help you.
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 7 years ago.
i want to create search suggestion. input type is created in this way:
<div class="signup_content_3">
<div class="signup_fieldName">
<p>
title
</p>
</div>
<div class="signup_text signup_input signup_search_parent" id="signup_flavorActor">
<input type="text" class="signup_searchType">
<div class="signup_suggestion">
</div>
</div>
my jQuery function to find key up is:
$('.signup_searchType').keyup(function(event){
$.ajax({url:"http://localhost:8000/auth/search/",success:function(result){
id=event.target.id;
var parent=event.target.parentElement.parentElement;
var add=parent.getElementsByClassName('signup_suggestion')[0];
add.style.display='block';
var child=add.firstChild;
while(add.firstChild){
add.removeChild(add.firstChild);
}
for(var i=0;i<Object.keys(result).length;i++){
var div=document.createElement('div');
div.innerHTML=result[i];
div.className='signup_oneSuggestion';
add.appendChild(div);
}
}})
});
and my jQuery click function is:
$('.signup_oneSuggestion').click(function(event){
console.log('click');
});
when i type in search type search suggestions appear but when i click on them does not print anything in console.
Use .on() (jQuery 1.7) with the element's ancestor, since it is created dynamically
$('.signup_suggestion').on('click', '.signup_oneSuggestion', function(event) {
console.log('click');
});
Dynamically created elements can be assigned an event in this way
$('body').on('click', '.signup_oneSuggestion', function(event) {
console.log('click');
});
This question already has answers here:
Why does click event handler fire immediately upon page load?
(4 answers)
Closed 9 years ago.
Given HTML such as
<div class="tpl grey">Hosts:
<p>Hi !</p>
<p>How are you ?</p>
<p>What ever.</p>
An other child & element type !
</div>
How to make that a click on a child element toggle the class="grey" of the closest parent .tpl element ?
The following code fails :
//Action to do on parent $(".tpl")
var switch1 = function () {
$(this).closest(".tpl").toggleClass("blue grey");
}
// On() click event
$(document).ready(function() {
$(".tpl").on("click", "p", switch1() );
});
Fiddle: http://jsfiddle.net/MRcCy/1
If you just want to toggle the closest .tpl (even though i only see one) try this
$(document).ready(function() {
$(".tpl p").click(function(){
$(this).closest('.tpl').toggleClass('grey');
});
});
Check this fiddle
$(document).ready(function() {
$(".tpl").click(function(){
$('.tpl').toggleClass('grey blue');
});
});