jQuery: Can I automatically apply a plug-in to a dynamically added element? - jquery-plugins

I'm in the process of converting my web app to a fully AJAX architecture.
I have my master page that is initially loaded and a div container that is loaded with dynamic content.
I created a few jQuery plugins that I apply to certain elements in order to extend their functionality. I'd normally call the functions as follows during each page load:
$(document).ready(function () {
// Enable fancy AJAX search
$(".entity-search-table").EntitySearch();
});
This would find the appropriate div(s) and call the plugin to enable the necessary functionality.
In an AJAX environment I can't just apply the plugin during the page load since elements will be added and removed dynamically.
I'd like to do something like this:
$(document).ready(function () {
// Enable fancy AJAX search
$(".entity-search-table").live("load", function () {
$(this).EntitySearch();
});
});
Question: Is there any way that I can trigger an event when a <div> or other element that matches a selector is added to the DOM?
It seems incredibly wasteful to activate the plug-in every time an AJAX request completes. The plug-in only needs to be applied to the element once when it is first added to the DOM.
Thanks for any help!

Yes - take a look at liveQuery. Example:
$('.entity-search-table').livequery(function(){
$(this).EntitySearch();
});

It seems incredibly wasteful to activate the plug-in every time an AJAX request completes. The plug-in only needs to be applied to the element once when it is first added to the DOM.
You can get the best of both worlds here, for example:
$("#something").load("url", function() {
$(".entity-search-table", this).EntitySearch();
});
This way it's only applying the plugin to the .entity-search-table elements you just loaded, since we specified a context to $(selector, context) to limit it.

The DOM 2 MutationEvent is what you really want, but unfortunately it isn't supported by IE. You'll need to either use live()/ delegate() binding in the plug-in, or (as I did when I had to work around this) use callbacks from your AJAX loaders indicating the scope of what has changed.

Use the live binding in your plugin code directly
jQuery.fn.EntitySearch = function() {
this.live(..., function(){ your plugin code });
return this;
}

Related

Add pagelink via ajax in tapestry

After an ajax call, I want to inject some element (list) in page. For each element of the list, I want to attach t:pagelink. I use PrototypeJs as js framework. Have you any idea?
I have already test the code below but doesn't work (it not render the t:pagelink)
new Ajax.Request('my_service_url', {
onSuccess: function(response) {
response.responseJSON.data.each(function(item){
var li = '<li>'+
'<span>'+item.title+'</span>'+
'<t:pagelink page="examples/navigation/PageLinks2">'+item.link+'</t:pagelink>'+
'</li>';
$('mylist').insert(li);
});
}
});
Thanks for your reply but I've found the solution. Just use processReply and it's ok. Indeed, the response needed a bit modification.
This does not work with Tapestry, as Lance Java pointed out, because the Javascript snippet is executed on the client. The client can't submit the <t:pagelink> tag to Tapestry for interpretation.
What you need to do instead is use the Ajax semantic of Tapestry, in particular the Zone component. <t:zone>...</t:zone> defines a section of your HTML document which will be replaced with a new version, rendered from .tml to html by Tapestry.
So in your <t:zone>, add <t:pagelink>'s as needed, probably dynamically, based on some condition or in some loop (i.e. with <t:if> or <t:loop> tags). Then, use some mechanism to send an AJAX event back to Tapestry. One simple option would be to use <t:actionlink zone="[ID of your zone]">.
See here for more info on Zones.

The view area of ckEditor sometimes shows empty at the start

I am using the following directive to create a ckEditor view. There are other lines to the directive to save the data but these are not included as saving always works for me.
app.directive('ckEditor', [function () {
return {
require: '?ngModel',
link: function ($scope, elm, attr, ngModel) {
var ck = ck = CKEDITOR.replace(elm[0]);
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
setTimeout(function () {
ck.setData(ngModel.$modelValue);
}, 1000);
}; }
};
}])
The window appears but almost always the first time around it is empty. Then after clicking the [SOURCE] button to show the source and clicking it again the window is populated with data.
I'm very sure that the ck.setData works as I tried a ck.getData and then logged the output to the console. However it seems like ck.setData does not make the data visible at the start.
Is there some way to force the view window contents to appear?
You can call render on the model at any time and it will simply do whatever you've told it to do. In your case, calling ngModel.$render() will grab the $modelValue and pass it to ck.setData(). Angular will automatically call $render whenever it needs to during its digest cycle (i.e. whenever it notices that the model has been updated). However, I have noticed that there are times when Angular doesn't update properly, especially in instances where the $modelValue is set prior to the directive being compiled.
So, you can simply call ngModel.$render() when your modal object is set. The only problem with that is you have to have access to the ngModel object to do that, which you don't have in your controller. My suggestion would be to do the following:
In your controller:
$scope.editRow = function (row, entityType) {
$scope.modal.data = row;
$scope.modal.visible = true;
...
...
// trigger event after $scope.modal is set
$scope.$emit('modalObjectSet', $scope.modal); //passing $scope.modal is optional
}
In your directive:
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
};
scope.$on('modalObjectSet', function(e, modalData){
// force a call to render
ngModel.$render();
});
Its not a particularly clean solution, but it should allow you to call $render whenever you need to. I hope that helps.
UPDATE: (after your update)
I wasn't aware that your controllers were nested. This can get really icky in Angular, but I'll try to provide a few possible solutions (given that I'm not able to see all your code and project layout). Scope events (as noted here) are specific to the nesting of the scope and only emit events to child scopes. Because of that, I would suggest trying one of the three following solutions (listed in order of my personal preference):
1) Reorganize your code to have a cleaner layout (less nesting of controllers) so that your scopes are direct decendants (rather than sibling controllers).
2) I'm going to assume that 1) wasn't possible. Next I would try to use the $scope.$broadcast() function. The specs for that are listed here as well. The difference between $emit and $broadcast is that $emit only sends event to child $scopes, while $broadcast will send events to both parent and child scopes.
3) Forget using $scope events in angular and just use generic javascript events (using a framework such as jQuery or even just roll your own as in the example here)
There's a fairly simple answer to the question. I checked the DOM and found out the data was getting loaded in fact all of the time. However it was not displaying in the Chrome browser. So the problem is more of a display issue with ckEditor. Strange solution seems to be to do a resize of the ckEditor window which then makes the text visible.
This is a strange issue with ckeditor when your ckeditor is hidden by default. Trying to show the editor has a 30% chance of the editor being uneditable and the editor data is cleared. If you are trying to hide/show your editor, use a css trick like position:absolute;left-9999px; to hide the editor and just return it back by css. This way, the ckeditor is not being removed in the DOM but is just positioned elsewhere.
Use this java script code that is very simple and effective.Note editor1 is my textarea id
<script>
$(function () {
CKEDITOR.timestamp= new Date();
CKEDITOR.replace('editor1');
});
</script>
Second way In controller ,when your query is fetch data from database then use th
is code after .success(function().
$http.get(url).success(function(){
CKEDITOR.replace('editor1');
});
I know, that this thread is dead for a year, but I got the same problem and I found another (still ugly) solution to this problem:
instance.setData(html, function(){
instance.setData(html);
});

What is the right away to update a panel using AJAX?

In MVC4 applications, I would like to update a panel using AJAX but using jQuery methods instead using AjaxExtensions from MVC.
But my problem is the updatePanelId.
I've seen several people use this to update it when has success:
success: function (response) {
var $target = $("#target");
var $newHtml = response;
$target.replaceWith($newHtml);
}
But when I do this, it forces me to use in every partial view that includes the id="target" at the root level of my razor view, and I guess that's not a good practice; I said this because I've realized when I use AjaxExtensions it doesn't happens, replace the update and it does not remove the panelId. But using jQuery it does.
Any idea to port the AjaxExtensions feature to jQuery?
You can use just:
$("#target").html(response); // it will just update content of the $("#target") container
Use jQuery's .load function. This will load the contents of the URL you specify into the target element. You can optionally specify a selector after the URL in load to only grab part of the target page.
$(function() {
$("#target").load("/MyURL");
});
JavaScript same origin policy applies to this.

Use Ajax in Prototype.js to load PART of an external page into a div

I'm looking for a way to load a part of an external page (possibly selected by an id in the external page) into a div. Something similar to Ajax.Updater, but with the option of specifying an id to look for in the external page.
Does anything like this exist in prototype. I've been googling for examples without luck. If I can't find it soon I'll have to do some "gymnastics" with Ajax.Request and some function tied to onSuccess.
You could do something like this, though it is by no means an elegant solution.
new Ajax.Updater("container", 'www.babes.com', {
onSuccess: function() {
$("container").update( $('idOfSomeLoadedElement') );
}
});
I don't think there is an actual elegant way of doing this purely in js. Ideally, you'd make your AJAX request only for what you need. You might be able to do some server-side stuff to lop out what you don't need (basically, offload the onsuccess functionality above to the server).
Instead of AJAX you might get by with an iframe.
// dollar function calls Element.extend
var iframe = $(document.createElement('iframe'));
// control how and what it loads
iframe.addEventListener('onLoad', function() {
$('container').update(iframe.contentDocument.select('#someID').first());
});
iframe.setAttribute('src', 'http://URL');
// add it as invisible, content isn't loaded until then
iframe.setAttribute('hidden', true);
document.body.appendChild(iframe);
From a minimal test it's clear that you'll have to be as concious about cross-origin policies as any AJAX method, that is, it's a PITA.

loading colorbox from within AJAX content

Firstly I am very new to all forms of javascript, particularly anything remotely AJAX. That said, over the course of the last day I have managed to code a script that dynamically refreshes a single div and replaces it with the contents of a div on another page.
The problem however is that several of my other scripts do not work in the ajax refreshed content. The most important of which being "colorbox".
I have spent several hours this evening researching this and am seeing lot's of stuff regarding .load, .live... updating the DOM on refresh etc...etc... But to be quite honest most of it is going over my head currently and I wouldn't know where to begin in terms of integrating it with the code I currently have.
My Ajax refresh code is as follows (My apologies if I haven't used best practice, it was my first attempt):-
$(function() {
$(".artist li.artist").removeClass("artist").addClass("current_page_item");
$("#rightcolumnwrapper").append("<img src='http://www.mywebsite.com/wp-content/images/ajax-loader.gif' id='ajax-loader' style='position:absolute;top:400px;left:190px;right:0px;margin-left:auto;margin-right:auto;width:100px;' />");
var $rightcolumn = $("#rightcolumn"),
siteURL = "http://" + top.location.host.toString(),
hash = window.location.hash,
$ajaxSpinner = $("#ajax-loader"),
$el, $allLinks = $("a");
$ajaxSpinner.hide();
$('a:urlInternal').live('click', function(e) {
$el = $(this);
if ((!$el.hasClass("comment-reply-link")) && ($el.attr("id") != 'cancel-comment-reply-link')) {
var path = $(this).attr('href').replace(siteURL, '');
$.address.value(path);
$(".current_page_item").removeClass("current_page_item");
$allLinks.removeClass("current_link");
$el.addClass("current_link").parent().addClass("current_page_item");
return false;
}
e.preventDefault();
});
$.address.change(function(event) {
$ajaxSpinner.fadeIn();
$rightcolumn.animate({ opacity: "0.1" })
.load(siteURL + event.value + ' #rightcolumn', function() {
$ajaxSpinner.fadeOut();
$rightcolumn.animate({ opacity: "1" });
});
});});
I was hoping someone might be kind enough to show me the sort of modifications I would need to make to the above code in order to have the colorbox load when the contents of #rightcolumn have been refreshed.
There is also a second part to this question. My links to the pictures themselves are now also being effected by the hashtag due to the above code which will in turn prevent the images themselves from loading correctly in the colorbox I should imagine. How can I prevent these images from being effected and just have them keep the standard URL. I only want the above code to effect my internal navigation links if at all possible.
Many thanks guys. I look forward to your replies.
That's a lot of code to review so I'll focus first on the conceptual side of things. Maybe that you will give you some clues...
It sounds like when you load content via Ajax the DOM is changed. No worries, that's kind of what we expect. However, scripts loaded before the Ajax calls may have difficulty if they are bound to elements that weren't there at page load time or are no longer there.
JQuery's live function is one solution to that. Instead of binding to a specific element (or collection of elements) at particular point in time, live lets you specify a binding to an element (or collection) of elements without regard to when they show up in the DOM (if ever).
ColorBox, however, in its default "vanilla" use abstracts that all away and, I believe, uses classic DOM binding - meaning the elements must be present at bind time. (Since you don't show your call to ColorBox I can't see how your using it.)
You may want to consider re-initalizing ColorBox after each content load by Ajax to be certain the binding happens the way you need it to.
Use $('selector').delegate() it watches the DOM of 'selector' and .live() is deprecated.
Use this to watch your elements AND fire the colorbox initilization. This way the colorbox is not dependent on the DOM element, but the other way around.
$("body").delegate("a[rel='lightbox']", "click", function (event) {
event.preventDefault();
$.colorbox({href: $(this).attr("href"),
transition: "fade",
innerHeight: '515px',
innerWidth: '579px',
overlayClose: true,
iframe: true,
opacity: 0.3});});
This should basically solve your problem and is cross browser tested.
The a[rel='lightbox'] in the delegate closure is the reference to what ever link you're clicking to fire the colorbox, whether it has been loaded with the initial DOM or with an AJAX request and has been added to the DOM in a live fashion. ie: any tag like this:
<a rel='lightbox' href="http://some.website.com">Launch Colorbox</a>

Resources