This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 1 year ago.
I'm receiving an ajax response, and based on this adding some html content to the dom.
Problem: I want to also add a link that should have a onclick function with passing a parameter from the response.
Example:
http://jsfiddle.net/zu1ymrh8/55/
<a class="LoadFromAjax" href="#">Load Ajax</a>
<div class="appendedContainer"></div>
$(function() {
function myfunc(input) {
alert("test: " + input);
}
$(".LoadFromAjax").on("click", function() {
event.preventDefault();
$.ajax({
url: 'https://dog.ceo/api/breeds/list/all',
type : 'get',
complete : function( qXHR, textStatus ) {
var mock = "John"; //some values extracted from rsp
$('.appendedContainer').hide();
$('.appendedContainer').append(`<a href='#' onclick='myfunc(${mock})' class='mylink'>Alert me</a>`);
$('.appendedContainer').fadeIn();
}
});
});
});
Result: ReferenceError: myfunct is not defined. Why?
I suggest setting the click listener on the container element.
See example below
document.querySelector('button').addEventListener('click', load);
const container = document.querySelector('.container');
// delegating the click handler to the container
container.addEventListener('click', handleItemClick);
function load() {
Promise
.resolve(Date.now())
.then(entry => {
const child = document.createElement('a');
child.innerText = entry;
container.append(child);
})
}
function handleItemClick(ev) {
console.log(ev.target.innerText)
}
a {
display: block;
}
<button>Load</button>
<div class="container"></div>
How about something like:
$.ajax({
...,
complete : function( qXHR, textStatus ) {
var mock = "John"; //some values extracted from rsp
// create a link:
var link = $('<a>', {
href : "the-url",
class : "the-class",
});
// append to container:
$('.appendedContainer').append(link);
// set on click function:
link.click(function() {
// do something with mock:
console.log(mock);
});
...
}
});
Related
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 11 months ago.
I am using jQuery v3.6.0 on a page that should dynamically allow a user to bookmark/unbookmark an item displayed on the page.
Here is my code snippet:
HTML
<div id="37.3" class="social-info-container">
<div id="VK7n" class="social bookmarkable">
<i title="Bookmarks" class="fa fa-bookmark"></i>0
</div>
</div>
Javascript
function bookmarkItem(id, elem_id){
let [ctid, oid] = id.split('.');
console.log(`Posting bookmark for item with content_type: ${ctid}, object_id: ${oid}`);
$.ajax({
type: "POST",
headers:{'X-CSRFToken': special_csrf_token },
url: social_bookmark_url,
data: {
cid: ctid,
oid: oid,
note: ''
},
dataType: "json",
success: function(resultData) {
console.log(`Reponse ok: ${resultData.ok}`);
console.log(`Elem id: ${elem_id}`);
$(`div#${elem_id}.social.bookmarkable > i.fa.fa-bookmark`).toggleClass('bookmarked');
let orig_html = $(`div#${elem_id}`).html();
let old_count = $(`div#${elem_id}`).text();
let new_count = resultData.count;
let new_html = orig_html.replace(old_count,new_count)
console.log(old_count);
console.log(orig_html);
console.log(new_count);
console.log(new_html);
$(`div#${elem_id}`).html(new_html);
},
error: function(xhr, status, error) {
if (xhr.status == 403){
window.location.href = login_url;
}
else {
alert(`Something went wrong: ${xhr.statusText}`);
}
}
});
}
$().ready(function(){
$('.social.bookmarkable .fa.fa-bookmark').on('click', function(e) {
alert('Clicked!');
let elem_id = $(this).closest('div.social.bookmarkable').attr('id');
console.log(`Elem id (1): ${elem_id}`);
let id = $(this).closest('div.social-info-container').attr('id');
bookmarkItem(id, elem_id);
});
});
When I click the bookmark icon, it works ONCE - after that I have to refresh the page to get it to work again. I thought using the on() method to bind to the click event would avoid this problem.
Why is the event being triggered just once - and how do I fix this?
You're replacing the element that the event handler is bound to. Using .on() will only work if the element you bind it to is still around. You need to also delegate the handler to an element that will always exist, and then use eg selector context to filter for the element you want.
function bookmarkItem(id, elem_id) {
let [ctid, oid] = id.split('.');
console.log(`Posting bookmark for item with content_type: ${ctid}, object_id: ${oid}`);
// Fake response to the AJAX call
let resultData = {
ok: 'ok',
count: 1
};
$(`div#${elem_id}.social.bookmarkable > i.fa.fa-bookmark`).toggleClass('bookmarked');
let orig_html = $(`div#${elem_id}`).html();
let old_count = $(`div#${elem_id}`).text();
let new_count = resultData.count;
let new_html = orig_html.replace(old_count,new_count)
console.log('old_count', old_count);
console.log('orig_html', orig_html);
console.log('new_count', new_count);
console.log('new_html', new_html);
$(`div#${elem_id}`).html(new_html);
}
$().ready(function(){
// Attach handler to an element which does not go away, and filter
// to only match clicks on the element you want.
// https://api.jquery.com/jquery/#selector-context
$('.social.bookmarkable').on('click', '.fa.fa-bookmark', function(e) {
let elem_id = $(this).closest('div.social.bookmarkable').attr('id');
let id = $(this).closest('div.social-info-container').attr('id');
console.log('Bookmark clicked: elem_id', elem_id, '; id', id);
bookmarkItem(id, elem_id);
});
});
.bookmarked {
background-color: black;
color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="37.3" class="social-info-container">
<div id="VK7n" class="social bookmarkable">
<i title="Bookmarks" class="fa fa-bookmark">Click me</i>0
</div>
</div>
There are many examples of this here on SO already, searching for keywords like "jquery only once", or "jquery replace handler once" turns them up. I know this well bcs I've done that search in the past :-) Eg:
jQuery - `on` event doesn't work after jQuery.replaceWith
jquery click only firing once
JQuery click event works only once
Jquery .change() event fires only once
This question already has answers here:
Passing data to a jQuery event handler
(4 answers)
Closed 1 year ago.
How can I pass modalData variable to clickOutsideModalHandler() event handler without getting any errors?
$('.modal').on('shown.bs.modal', function() {
var modalData = {
confirmText: $(this).data('confirm-text'),
cancelText: $(this).data('cancel-text'),
confirmMessage: $(this).data('confirm-message')
};
$(document).click(clickOutsideModalHandler);
});
$('.modal').on('hide.bs.modal', function() {
$(document).unbind('click', clickOutsideModalHandler);
});
function clickOutsideModalHandler() {
var obj = $(".modal-content");
if (!obj.is(event.target) && !obj.has(event.target).length) {
var formFieldset = obj.find("form").find("fieldset").attr("disabled");
if(formFieldset !== "disabled") {
yii.confirm("send form?", function(){
obj.find("form").submit();
});
}
}
}
unbind is deprecated (you should use off instead).
And using event in your clickOutsideModalHandler relies also on a deprecated feature Window.event, you should use the event passed as argument to the callback.
The on function has this signature .on( events [, selector ] [, data ], handler )
data
Type: Anything
Data to be passed to the handler in event.data when an event is triggered.
So you can set the data to be passed with the event using the data parameter:
$('.modal').on('shown.bs.modal', function() {
var modalData = {
confirmText: 'test',
cancelText: 'test',
confirmMessage: 'test',
};
$(document).on('click', modalData, clickOutsideModalHandler);
});
$('.modal').on('hide.bs.modal', function() {
$(document).off('click', clickOutsideModalHandler);
});
function clickOutsideModalHandler(event) {
// access the data using the data property of the event object
console.dir(event.data);
// rest of you code
}
// that part is just here to make the snippet functional
$('.modal').trigger('shown.bs.modal')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<div class="modal">modal</div>
<div>some other content</div>
Hello I have loaded div via ajax and wanted to give javascript eventlistener with addEventListener method but this not working. Here below is my code
var QuantityMiniCart = function() {
var infor = document.querySelectorAll( '.mini-cart-product-infor' );
if ( ! infor.length ) {
return;
}
};
(function () {
document.addEventListener('DOMContentLoaded',function () {
QuantityMiniCart();
})
})();
infor.forEach(
function( ele, i ) {
input = ele.querySelector( 'input.qty' ),
}
// Check valid quantity.
input.addEventListener(
'change',
function() {
}
);
}
);
here is ajax code
$.ajax({
type: 'POST',
url: add_mini_cart_ajax.ajax_url,
data: {
action : 'mode_theme_update_mini_cart'
},
success: function( response ) {
$('.confirm-product').html(response);
},
error: function(e) {
console.log(e)
return;
}
});
The .confirm-product containing .mini-cart-product-infor which is loading from ajax. Please help for this
querySelectorAll can only select elements which exist at the time that command is run. It can't do anything which elements which don't exist yet!
So if you're loading more content via AJAX, after you've run the code shown in your question, then you'll need to separately add event listeners to any newly-downloaded elements, once the AJAX call is complete.
In my plug-in I need to wrapp all sidebar's children in a div to let them overflow but if those elements are loaded dynamically the function does not work and I don't know either how to make it work.
The code is:
<div class="sidebar">
</div>
var $sidebar = $( '.sidebar' );
$sidebar.load( 'external-page.ext' );
$sidebar.MyPlugin();
$.fn.MyPlugin = function() {
this.wrapInner( '<div />' );
});
If those elements are not loaded dynamically there is no problem.
Firstly the code was:
$sidebar.wrapInner( '<div/>' );
and this just works fine if elemens are not loaded dynamically, so I tried this way:
var children = $sidebar.children();
$( document ).on( 'load', children, function() {
$( this ).wrapAll( '<div />' );
});
but, of course it does not work.
Can you please help me?
I thought that this rule would have worked this time too but it didn't. What did I mistake?
You can find the whole code here.
And a demo here
MORE DETAILS
I want to handle this issue from the inside, not from the outside! I don't know if users will load content dinamically or not. that's the point.
So there is a way to handle this issue inside the plugin and not outside?
From the manual
http://api.jquery.com/load/
Callback Function
If a "complete" callback is provided, it is executed after
post-processing and HTML insertion has been performed. The callback is
fired once for each element in the jQuery collection, and this is set
to each DOM element in turn.
Try the following code and see if this works:
$sidebar.load( 'external-page.ext', function() { $sidebar.MyPlugin(); } );
Thanks.
$.load() makes an ajax call to load the data ,
So there is a possibility that your this.wrapInner( '<div />' ) method has invoked before any data is loaded inside the div.sidebar.
Make sure this.wrapInner( '<div />' ) is called after all data has been loaded successfully using the complete callback.
$.load() trigger callback for each div ,call your plugin from callback
$sidebar.load('http://fiddle.jshell.net/vikrant47/ncagab2y/1/show/', function () {
$(this).MyPlugin();
}
});
DEMO
OR
If you are using $.load() only to load inside multiple elements then you could probably use one of the more powerful jQuery ajax methods (i.e., get() or post() or ajax()).
$.get('http://fiddle.jshell.net/vikrant47/ncagab2y/1/show/', {}, function(data) {
$sidebar.html(data).MyPlugin();
});
DEMO using $.get() Method
UPDATE-
Answer to the comment-
You should not have to worry about weather user gonna call your plugin like this $sidebar.load(...).MyPlugin().User must be aware enough about how to handle asynchronous methods.
You can not make your plugin work until there is some data inside div.slider
but ,you can add ajax loading functionality inside your plugin like -
$(document).ready(function () {
$.fn.MyPlugin = function (options) {
var $elem=this;
var init = function () {
options.load = $.extend({}, $.fn.MyPlugin.defaultOptions.load, options.load);
load();
}
//adding load method to load data dynamically
var load = function () {
if (!options.load.url) {
alert("url can not be empty");
} else {
$.ajax({
url: options.load.url,
type: options.load.type,
data: options.load.data,
success: function (response) {
options.load.success.call(this, response);
$elem.html(response).wrapInner('<div class="wrapper"/>');//wrap after data has been loaded successfully
},
error : function (jqXHR, textStatus, errorThrown) {
alert("error occured" + textStatus + " ," + errorThrown)
}
})
}
}
init();
}
$.fn.MyPlugin.defaultOptions = {
load: {
tye: "get",
data: {},
success: function () {}
}
};
Now use your plugin like-
var $sidebar = $('.sidebar');
$sidebar.MyPlugin({
load: {
url: 'http://fiddle.jshell.net/vikrant47/ncagab2y/1/show/'
}
});
});
DEMO with load
Try adding adding below piece to plugin . Added at lines 84 - 110 at gist .
var target = $sidebar.get(0);
// create an observer instance
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
// do stuff when
// `childList` modified
// i.e.g.,
$.each(mutation.addedNodes, function (k, v) {
$(v)
.wrapInner('<div data-'
+ dataName
+ '="sub-wrapper"></div>')
})
});
});
// configuration of the observer:
var _config = {
childList: true
};
// pass in the target node, as well as the observer options
observer.observe(target, _config);
jsfiddle http://jsfiddle.net/guest271314/s5wzptc8/
See MutationObserver
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 8 years ago.
I have filter for producttypes which is getting created as per enteries in database via ajax:
HTML Part:
<ul id="foodtype"></ul>
Ajax Code to show filter:
function showtypes(){
$.ajax({
type: "POST",
url: "ajax/incl/showtype",
data:'',
dataType : 'json',
cache: false,
success: function(records1){
$('#foodtype').html(makefoodType(records1));
}
});
}
function makefoodType(data1){
var tbl_body = "";
$.each(data1, function() {
var tbl_row = "",
currRecord = this;
$.each(this, function(k1 , v1) {
tbl_row += "<li><input type=checkbox id="+v1+" name="+v1+" />
<span style=font-size:14px>"+v1+"</span></li>";
})
tbl_body += tbl_row;
})
return tbl_body;
}
The categories are getting displayed properly but when i select checkbox then following code needs to be executed
function getFilterOptions(){
var opts = [d,de];
$checkboxes.each(function(){
if(this.checked){
opts.push(this.id);
}
return opts;
}
var $checkboxes = $("input:checkbox");
$checkboxes.on("change", function(){
var opts = getFilterOptions();
updateProducts(opts);
});
I want the ID of checkbox to be pushed to page having php code. But nothing is happening on checking checkbox.
Note: When i view source code then <ul id="foodtype"></ul> code remains inact.. No <li> elements are displayed :(
You need to use something called event delegation. Listen on the click events on the container where checkboxes are. So after you add them dynamically, the click event will get bubbled to the container.
That's because var $checkboxes = $("input:checkbox"); is getting executed before ajax is complete and hence no elements.
Change to
$(document).on("change", "input:checkbox", function() {
var opts = getFilterOptions();
updateProducts(opts);
});
and it should work.