Dynamically added elements don't wrap - javascript

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

Related

javascript eventlistener not working for ajax loaded content

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.

AJAX jQuery on click dynamically created only works first time

I am trying to create a dropdown menu that I dynamically insert into using jQuery. The objects I'm inserting are notifications, so I want to be able to mark them as read when I click them.
I have an AJAX call that refreshes the notifications every second from the Django backend.
Once it's been refreshed, I insert the notifications into the menu.
I keep an array of the notifications so that I don't create duplicate elements. I insert the elements by using .append(), then I use the .on() method to add a click event to the <li> element.
Once the click event is initiated, I call a function to .remove() the element and make an AJAX call to Django to mark the notification as read.
Now my problem:
The first AJAX call to mark a notification as read always works. But any call after that does not until I refresh the page. I keep a slug value to identify the different notifications.
Every call I make before the refresh uses the first slug value. I can't figure out why the slug value is tied to the first element I mark as read.
Also, if anyone has a better idea on how to approach this, please share.
Here's my code:
var seen = [];
function removeNotification(elem, urlDelete) {
elem.remove();
console.log("element removed");
$.ajax({
url: urlDelete,
type: 'get',
success: function(data) {
console.log("marked as read");
},
failure: function(data) {
console.log('failure to mark as read');
}
});
}
function insertNotifications(data) {
for (var i = 0; i < data.unread_list.length; i++) {
var slug = data.unread_list[i].slug
var urlDelete = data.unread_list[i].url_delete;
if (seen.indexOf(slug) === -1) {
var elem = $('#live-notify-list').append("<li id='notification" +
i + "' > " + data.unread_list[i].description + " </li>");
var parent = $('#notification' + i).wrap("<a href='#'></a>").parent();
seen.push(slug);
$( document ).ready(function() {
$( document ).on("click", "#notification" + i, function() {
console.log("onclick " + slug);
removeNotification(parent[0], urlDelete);
});
});
}
}
}
function refreshNotifications() {
$.ajax({
url: "{% url 'notifications:live_unread_notification_list' %}",
type: 'get',
success: function(data) {
console.log("success");
insertNotifications(data);
},
failure: function(data) {
console.log('failure');
}
});
}
setInterval(refreshNotifications, 1000);
I really don't know what do you mean with parent[0] in
removeNotification(parent[0], urlDelete);
I think you can simply try $(this)
removeNotification($(this), urlDelete);
but to be honest I find to put
$( document ).ready(function() {
$( document ).on("click", "#notification" + i, function() {
console.log("onclick " + slug);
removeNotification(parent[0], urlDelete);
});
});
inside a loop .. its bad thing try to put it outside a function and use it like
$( document ).ready(function() {
setInterval(refreshNotifications, 1000);
$( document ).on("click", "[id^='notification']", function() {
console.log("onclick " + slug);
removeNotification($(this), urlDelete);
});
});
and try to find a way to pass a urlDelete which I think it will be just one url

Dealing with detached dom elements, gridster.js plugin

I am working with the gridster.js plugin, which is great, however it seems to be giving me issues when I am removing items using it's remove method. I am using its built in method called remove_all_widgets to wipe out what I currently have on the page and load in new content.
You can see it here.
fn.remove_all_widgets = function(callback) {
this.$widgets.each($.proxy(function(i, el){
this.remove_widget(el, true, callback);
}, this));
return this;
};
It loops through the widgets and calls this remove_widget as seen here:
/**
* Remove a widget from the grid.
*
* #method remove_widget
* #param {HTMLElement} el The jQuery wrapped HTMLElement you want to remove.
* #param {Boolean|Function} silent If true, widgets below the removed one
* will not move up. If a Function is passed it will be used as callback.
* #param {Function} callback Function executed when the widget is removed.
* #return {Class} Returns the instance of the Gridster Class.
*/
fn.remove_widget = function(el, silent, callback) {
var $el = el instanceof $ ? el : $(el);
var wgd = $el.coords().grid;
// if silent is a function assume it's a callback
if ($.isFunction(silent)) {
callback = silent;
silent = false;
}
this.cells_occupied_by_placeholder = {};
this.$widgets = this.$widgets.not($el);
var $nexts = this.widgets_below($el);
this.remove_from_gridmap(wgd);
$el.fadeOut($.proxy(function() {
$el.remove();
if (!silent) {
$nexts.each($.proxy(function(i, widget) {
this.move_widget_up( $(widget), wgd.size_y );
}, this));
}
this.set_dom_grid_height();
if (callback) {
callback.call(this, el);
}
}, this));
return this;
};
I have small bits of javascript to run button functions and other assorted things. I realized soon after playing with it that it leaves the full html content of the widget in a detached dom tree thus keeping the js files running. I first found this because the buttons have the same names on new pages and it was running the click functions for both the newly loaded button and the one i had taken off the screen using gridsters remove_all_widgets method.
I can track the previous javascript to an (anonymous function) in chomes dev console, and within that i can see the entire html content inside of the detached tree. I am not refreshing the pages or anything, the new content is being brought in by ajax (I set ajax cache:false as well).
Is there any way around this? Would it be possible to clear the contents of the widgets before they get stuck like this? It would be ideal if it didn't happen at all or of there was some way to get rid of them completely when they get removed.
Thank you for taking the time to read this, any insight would be greatly helpful.
As per requests her is some of the code, the click functions on the lines button for example is double firing:
$(document).ready(function() {
$(document).on("change",".accountContries",function(e){
var countryPck = $("body > .addAccountForm1").find(".accountContries").find('option:selected').attr('id');
$.ajax(
{
cache: false,
url : "/listStates/" + countryPck,
type : "GET",
beforeSend: function(){
$("body").append("<div class='loadingNow'></div>");
},
success:function(data, textStatus, jqXHR) {
$('.loadingNow').remove();
$(".accountStates").empty();
$(".accountStates").append("<option value='' selected disabled> Select a State</option>");
$.each(data.states, function(){
$(".accountStates").append("<option value=" + this.id +" id=" + this.id + ">" + this.name +"</option>");
});
},
error: function(jqXHR, textStatus, errorThrown)
{
errorOffScreen("List States by account");
}
});
});
$(document).on("touchend click", ".lines-button", function(e){
e.stopImmediatePropagation();
if($(this).hasClass("close")){
$(this).removeClass("close");
$(".widget1x1Back").next(".actionsHolder3").slideUp("fast", function() {
$(this).remove();
});
}else{
var iconsList = $(this).closest(".top1x1").next(".hdnActnLst").find(".iconsHolder3").html();
$(this).closest(".widget1x1").append(iconsList);
$(this).closest(".widget1x1").find(".actionsHolder3").hide();
$(this).closest(".widget1x1").find(".actionsHolder3").slideDown(700,"easeOutBack");
$(this).addClass("close");
}
});
});
</script>
UPDATE : it seems I only the stuff inside the <Script> tag is being kept even after the elements are .removed
Based on your comments and updates, you have Javascript in the loaded content pages. As that includes the use of delegated event handlers, that code will live on.
This is one case were you would be better off with normal non-delegated event handlers like:
$(".lines-button").bind("touchend click", function(e){
e.stopImmediatePropagation();
These bindings will terminate when the elements are removed (as they are per-element handlers).
An alternative is to not have any code in the content pages, but only in the master page, and apply code as required when loading new content.

How to change text of jquery created tooltip?

I have an application that displays web pages which contains source reference, when user hover above the source reference a tooltip with extra information appears.
I want to change tooltip text dynamically to responseText if communication with the server was successful (see method below) - but I don't know how .
(I already make sure the the respomseText contains the right data)
the tooltip is generated and it's data is sent by this jQuery code:
$(document).ready(function() {
$('table a').on('mouseenter._do_submit', _do_submit);
$('table a').tooltip({
tooltipClass: "coolToolTip",
content: function() {
var element = $( this );
return '<p class=toolTipP>' +element.text(); + '</p>';
}
});
$('table').bind('mouseleave._remove_icon', _remove_icon);
function _remove_icon(event) { $(event.target).find('img').remove(); }
function _do_submit(event) {
$event_origin = $(event.target);
$event_origin.find('img').remove();
ajax_sender( $event_origin );
}
function ajax_sender(event_origin_obj) {
$('<img src="./js/ajax_ani.gif" />').appendTo(event_origin_obj);
url= 'http://localhost:8080/zoharTranslator/ReadZohar';
var xhr = $.ajax({
type: 'GET',
url: url,
data: 'command=source&src=' + event_origin_obj.text(),
beforeSend: beforeSending,
success: on_success,
error: log_error_message
});
function on_success(data) {
event_origin_obj.find('img').remove();
$(document).removeAttr("title");
event_origin_obj.attr( "title", data);
console.log(xhr);
}
function log_error_message(xhr) {
...
}
function beforeSending(xhr) {
...
}
}
});
You can always change the content of your tooltip after its created by using it's setter like this...
$( ".selector" ).tooltip( "option", "content", "Awesome title!" );
-- 2ND UPDATE --
OK, I figured out how to get the jQuery UI added to jsFiddle. This is how I would solve your problem: http://jsfiddle.net/spZ69/3/
Obviously the ajax call won't work but just replace the url ajax/test.html with your url that returns text and it will replace the tooltip's text.

reinitialize other javascript functions after loading a page with ajax pagination

Apologies, a total newb here. How can I load other plugins, and let other separate scripts function after loading an ajax generated page? This is my curent code:
jQuery(document).ready(function($) {
var $mainContent = $("load-content"),
siteUrl = "http://" + top.location.host.toString(),
url = '';
$(document).delegate("a[href^='"+siteUrl+"']:not([href*='/wp-admin/']):not([href*='/wp-login.php']):not([href$='/feed/'])", "click", function() {
if($.browser.msie){
var myie="/"+this.pathname;
location.hash = myie;
//alert(location.hash);
}else{
location.hash = this.pathname;
}
return false;
});
$("#searchform").submit(function(e) {
$search = $("#s").val();
$search = $.trim($search);
$search = $search.replace(/\s+/g,'+');
location.hash = '?s='+$search;
e.preventDefault();
});
$(window).bind('hashchange', function(){
url = window.location.hash.substring(1);
if (!url) {
return;
}
url = url + " #content";
$('html, body, document').animate({scrollTop:0}, 'fast');
$mainContent.fadeOut(500, function(){$('#content').fadeOut(500, function(){
$("#loader").show();});}).load(url, function() {
$mainContent.fadeIn(500, function(){
$("#loader").hide(function(){ $('#content').fadeIn(500);});});});
});
$(window).trigger('hashchange');
});
How can embedded objects on pages retain their functionality? Mainly videos, slideshows and other media that use javascript like
video js (html5 video player)
vimeo
and
portfolio slideshow for wordpress
When you load ajax-generated markup it will not retain the functionality it had before. In your example above, you're initialising things when the DOM is ready to be acted upon. In order to make sure any plugins, etc, are running after you perform the ajax request you need to reinitialise them.
Given your code sample above, I'd recommend a little restructuring. For example, you could create a function called init which you could call to initialise certain plugins:
function init () {
$("#plugin-element").pluginName();
}
jQuery(document).ready(function () {
// Initialise the plugin when the DOM is ready to be acted upon
init();
});
And then following this, on the success callback of you ajax request, you can call it again which will reinitialise the plugins:
// inside jQuery(document).ready(...)
$.ajax({
type: 'GET',
url: 'page-to-request.html',
success: function (data, textStatus, jqXHR) {
// Do something with your requested markup (data)
$('#ajax-target').html(data);
// Reinitialise plugins:
init();
},
error: function (jqXHR, textStatus, errorThrown) {
// Callback for when the request fails
}
});

Categories