This is my code to open a Fancybox that loads a page via ajax:
$('tr.record').click(function() {
var record_id = $(this).attr("id");
var link = 'http://' + window.location.hostname + '/expenses/expenses_edit/' + record_id;
$.fancybox({
'transitionIn': 'fade',
'transitionOut': 'none',
'type': 'ajax',
'href': link,
'onClosed': function() {
parent.location.reload(true);
}
});
$.bind("submit", function() {
$.fancybox.showActivity();
$.ajax({
type: "POST",
cache: false,
data: $(this).serializeArray(),
success: function(data) {
$.fancybox(data);
}
});
return false;
});
});
The page inside the fancybox has a form including a text field with char counter:
<form>
...
<textarea name="exp_det" cols="90" rows="12" id="exp_det"></textarea>
<span id="charLeft">150</span> characters left
</form>
The parent page loads this via header:
$('#ext_det').keyup(function() {
var len = this.value.length;
if (len >= 150) {
this.value = this.value.substring(0, 150);
}
$('#charLeft').text(150 - len);
});
So the issue is that this char counter (and other jQuery stuff like validation, datepicker) don't work inside the Fancybox. They do work if the page is loaded without Fancybox, or if it is loaded via Fancybox as inline.
I understand this seems to be a reasonably common question, but I tried all solutions and none worked for me.
I've tried
using diferent IDs for the ajax page
placing the char count script at the end of ajax page (it didn't even show
on Firebug)
placing the char count script as a function within success
other crazy iterations
The problem seems related to re-init'ing the jQuery functions after the ajax page has loaded, since these elements were not there when the parent page was loaded.
Any suggestions?
You need to either rebind your event handlers once you have loaded content inside the fancy box or use the JQuery.live event binding mechansim. Your problem is occuring because your event handlers are trying to bind to DOM nodes that are not yet loaded, so what you need to do is either use the JQuery.live, which will bind events to elements even when they have not yet loaded OR use a callback function when your content of your fancybox has loaded to bind the event handlers.
$(document).ready(function() {
$('#ext_det').live('keyup', function() {
// code to execute on keyup for element with id #ext_det
});
});
Related
I am trying to achieve something when i click the button. The button is loaded from the server using an AJAX call. However, nothing happens when the button is clicked. Here is my code:
(This ajax call is in JS Fiddle -- only for testing purposes)
JS Fiddle
Code:
<div id="target"></div>
new Request.HTML({
url: '/echo/html/',
data: {
html: "<div id='0001'>"+"<h5 class='title'>Hello World</h5>"+"<h4 class='date'>2014-07-19</h4>"+"<button> Add to Calendar </button>"+"</div>",
delay: 0
},
method: 'post',
update: 'target'
}).send();
$("button").click(function(){
var value = $(this).siblings(".title").text();
value += $(this).siblings(".date").text();
alert(value);
});
Since the button does not yet exist you can use jQuery.on to delegate an event handler.
$("body").on('click', 'button', function(){
var value = $(this).siblings(".title").text();
value += $(this).siblings(".date").text();
alert(value);
});
Also your fiddle does not work since you are using both jQuery and MooTools but only loading MooTools.
Added after seeing authors page:
The var value = $(this).siblings(".title").text(); selector will not work. I would recommend adding an event class (as in calendar event) to the wrapper and using:
var value = $(this).parents(".event").find(".title").text();
If you are adding elements to the DOM via AJAX (or any other method) and you want listeners to remain bound to them, then you need to attach those listeners to elements in the document that don't change - like $(document).
e.g.
$(document).on('click','button',function(e) {
//your code here
});
The problem with your code is that you're getting all the buttons by doing $("button") and then attaching click listeners to them. The problem is that when you're doing this, your button hasn't been attached to the document and, therefore, will not have a click listener attached to it.
If button doesn't exist on page load. You can;t use $("button") to address it. You have to surround it by something else(div for example) and do it like this:
$('div').on('click','button', function(){...})
There are 2 things you need to consider.
ONE - jQuery that is included with mootools.
I was surprised that $ function in mootools version that you are using gave me a very BASIC jQuery function. I tried higher versions of mootools and still the same happened. I am sure it is not jQuery at all and mootools is just using $ variable for their purpose. [Optional: So, you may also consider defining jQuery as a different variable in your script.]
So, I added a link to latest jQuery in External Resources on jsfiddle and then it worked. Do not forget to expand External Resources on left on jsfiddle. But, if mootools is using $ for its own purpose, then jQuery will surely overwrite it. Please consider using jQuery as a different variable than $.
TWO - Use the onSuccess function of AJAX request.
You call the AJAX request and so you have to wait for some time for browser to load it which depends on user's internet connection. So, this is why we use onError and onSuccess events. Write the button function in onSuccess because button does not exist on document until AJAX is loaded.
Please check this code and it works.
http://jsfiddle.net/U7ewu/
Code:
new Request.HTML({
url: '/echo/html/',
data: {
html: "<div id='0001'>"+"<h5 class='title'>Hello World</h5>"+"<h4 class='date'>2014-07-19</h4>"+"<button> Add to Calendar </button>"+"</div>",
delay: 0
},
method: 'post',
update: 'target',
onSuccess: function() {
$("button").click(function(){
var value = $(this).siblings(".title").text();
value += $(this).siblings(".date").text();
alert(value);
});
}
}).send();
EDIT:
Please use $.noConflict(); so that mootools and jquery do not conflict each other on $ variable. $ is already being used by mootols, so please use the word jQuery instead.
Please check this jsfiddle.
http://jsfiddle.net/wkMep/
Code:
$.noConflict();
new Request.HTML({
url: '/echo/html/',
data: {
html: "<div id='0001'>"+"<h5 class='title'>Hello World</h5>"+"<h4 class='date'>2014-07-19</h4>"+"<button> Add to Calendar </button>"+"</div>",
delay: 0
},
method: 'post',
update: 'target',
onSuccess: function() {
jQuery("button").click(function(){
var value = jQuery(this).siblings(".title").text();
value += jQuery(this).siblings(".date").text();
alert(value);
});
}
}).send();
try this
$("button").on("click",function(){
var value = $(this).siblings(".title").text();
value += $(this).siblings(".date").text();
alert(value);
});
I've been working on a little project that involves collecting/processing search results and I thought you guys may be able to lend a hand. I have a small script that takes a search value from a search input, processes it via PHP and then collects and inserts the results in a fancybox via JS. Thus far, all is going well but I can't seem to work out the next bit.
I can't manage to interact with any elements in the fancy box because it will reload the page (for example, previous and next buttons or search input). How would you go about loading new content or form inputs into a fancybox on the same page instance using AJAX?
HTML:
<form action="search.php" method="post" name="search" id="search">
<input size="35" value="" placeholder="Search" name="search" id="result" autocomplete="off">
<button id="check" data-fancybox-type="ajax" href="search/search.php" id="check">Search</button>
Script:
jQuery(document).ready(function(submit) {
$('#check').on("click", function (e) {
e.preventDefault();
$.ajax({
type: "POST",
cache: false,
url: "search.php",
data: $("#result").serializeArray(),
success: function (data) {
// on success, post returned data in fancybox
$.fancybox(data, {
// fancybox API options
fitToView: true,
openEffect: 'fade',
closeEffect: 'fade'
});
}
});
});
});
The above script is largely basses on this post
Thanks for your ideas!
The fancybox API provides a number of event based callback functions for interacting with the fancybox elements, before, during and after the box is shown or loaded. This allows you a lot of flexibility.
I would use the afterLoad() callback function within the Fancybox2 API.
See this fiddle i put together: Fancybox afterLoad() callback to return a function
This is just my solution, I am still very much a student of js, so I can't say this is the best way, and I welcome feedback or edits.
Basically we will use the afterLoad() API function of fancybox so that upon successful load of the fancybox element driven from your successful $.ajax call a function is returned to then listen to the click event of an element loaded into your fancybox.
$.fancybox(echoData, {
// fancybox API options
fitToView: true,
openEffect: 'fade',
closeEffect: 'fade',
afterLoad: function () {
return fancyUpdateMyWay = function () {
var a = Math.random();
var newHtml = "<h4>UPDATED=" + a + "</h4>";
$("#fancyResult").html(newHtml);
}
}
});
Note: I imagine that you will want to do further ajax calls from within the fancybox results, and here I only demonstrated a simple DOM update from jquery.
try changing your javascript to use 'click' instead of 'on'. this should do it
jQuery(document).ready(function(submit) {
$('#check').click(function (e) {
e.preventDefault();
$.ajax({
....
});
});
});
I am developing MVC 3 application and using razor syntax.
In this application I am giving commenting facility.
I have given the facility to adding a comment and it saved in DB.
and when user clicks on delete button it displays the message as "Clicked".
When user load entity, previously added comments get displayed on page with
delete button and when user click on that button the "clicked" msg appears.
now, when user add a new comment, it saved in DB sucsessfully and also appear on the page
along with Delete button.
now when user click on delete button msg wontcome...
( I append the Div tag while loading the new comment from DB)
I think , there is a issue regarding append, means previous comments Delete button
work well, but when I add button using append it wont works...
Here is the code, saves comment in DB and in sucsess , it creates HTML Code with button to disply the data on the page.
<script src="../../Scripts/jquery.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#AddCommentButton').click(function ()
{
if (document.getElementById('Comment').value != "")
$.ajax({
type: 'post',
url: '/Comment/SaveComments',
dataType: 'json',
data:
{
'comments' : $('#Comment').val(),
'EType' : #Html.Raw(Json.Encode(ViewBag.EType)),
'EId' : #Html.Raw(Json.Encode(ViewBag.EId))
},
success: function (data) {
$("p.p12").append('<button type="button" id = "1" class="deleteComment">Delete</button><br />')
alert(data.Id);
}
});
});
});
</script>
and user clicks on Delete Button I have written this code.
$(document).ready(function () {
$(".deleteComment").click(function ()
{
alert("Clicked");
});
});
For previous comments, when user click on the delete button "Clicked' msg comes but when user clicks on newly added comment's delete button, msg wont come ...
You need to subscribe to the click event of this delete button in a lively manner since it was added dynamically to the DOM. You cannot just use .click() in your document.ready because the delete button doesn't yet exist at this stage. So depending on the jQuery version that you are using there are 3 ways:
.on(), .delegate() or .live().
The recommended approach is .on() which is supported starting from jQuery 1.7:
$(document).on('click', '.deleteComment', function() {
alert("Clicked");
});
And you no longer need to wrap this in a document.ready.
If you are using an older version here's the same with .delegate() (introduced in jQuery 1.4.2):
$(document).delegate('.deleteComment', 'click', function() {
alert('Clicked');
});
And if you are using an even older version of jQuery, well, you should upgrade and if you don't want to upgrade use .live():
$('.deleteComment').live('click', function() {
alert('Clicked');
});
And while I am at your code here are a couple of other remarks.
Replace:
<script src="../../Scripts/jquery.js" type="text/javascript"></script>
with:
<script src="#Url.Content("~/Scripts/jquery.js")" type="text/javascript"></script>
and also replace:
url: '/Comment/SaveComments',
with:
url: '#Url.Action("SaveComments", "Comment")',
And by the way as an alternative to putting the url in your javascript you could directly use the value of your AddCommentButton. You haven't shown it your markup I assume that it might look like this:
#Html.ActionLink("Add a comment", "SaveComments", "Comment", null, new { id = "AddCommentButton" })
And now all that's left is to unobtrusively AJAXify it:
$(document).ready(function () {
$('#AddCommentButton').click(function (evt) {
evt.preventDefault();
var comment = $('#Comment').val();
if (comment == '') {
alert('Please enter a comment');
return;
}
$.ajax({
type: 'post',
url: this.href,
data: {
comments : comments,
EType: #Html.Raw(Json.Encode(ViewBag.EType)),
EId: #Html.Raw(Json.Encode(ViewBag.EId))
},
success: function (data) {
// You probably need to embed the comment id as a HTML data-* attribute
// to the button instead of using a hardcoded id="1" value
// which by the way is an invalid value of an id in HTML:
$('p.p12').append(
$('<button/>', {
'class': 'deleteComment',
'html': 'Delete',
'data-id': data.Id
}).after($('<br/>'))
);
}
});
});
});
and now inside your Delete button click callback you will be able to access the id of the comment to be deleted:
$(document).on('click', '.deleteComment', function() {
var commentId = $(this).data('id');
// TODO: delete the comment
});
Absolutely never hardcode urls in an ASP.NET MVC application. Always use url helpers to generate them. The reason for this is that url helpers take into account the routing setup and the virtual directory in which your application might be running. So if later you decide to change the pattern of your routes or even deploy your application in IIS you will no longer need to go through all your pages and replace those wrongly hardcoded urls for your application to work.
I need to keep displaying the loading gif until all images
in the returned data (html) have been finished loading.
The data and everything is returned properly, but the .live('load', function() {})
is never executed, have tried without the .live as well. The contents of #ContentBody
just get replaced with the returned html data (#LoadingLayer disappears too of course) and I can see images loading as usual.
<script type="text/javascript">
$("#RightLink").click(function (event) {
event.preventDefault();
var tourl = $(this).attr('data-ajax-url');
$.ajax({
type: "POST",
url: tourl,
dataType: "html",
async: true,
beforeSend: function () {
$("#LoadingLayer").show(); //show layer with image loading gif
$('#ColumnContainer').hide();
},
success: function (data) {
$('#ContentBody').html(data).live('load', function () { $("#LoadingLayer").hide(); });
}
});
});
</script>
HTML layout:
<body>
<div id="ContentBody">
<a id="RightLink" href="/store/ContentBodyGenerator" />
<div id="LoadingLayer"><img src="loading.gif" /></div>
<div id="ColumnContainer">... Main contents, lots of images here</div>
</div>
</body>
Why don't you just hide #LoadingLayer directly?
$('#ContentBody').html(data);
$("#LoadingLayer").hide();
Edit:
I misread your question, I don't think there is an easy way to detect that all images have been loaded. I suggest you try the waitForImages plugin.
Try changing the contents of the "success" function to this...
$('#ContentBody').html(data).live('load', function () {
var imgcount = $(this).find("img").length;
$(this).find("img").one("load", function() {
imgcount--;
if (imgcount == 0) {
$("#LoadingLayer").hide();
}
}).each(function() {
if (this.complete) $(this).load();
});
});
It waits till html(data) is loaded and then gets an image count. It then adds an event handler to each of the images to decrement the image count when the image is loaded. The one("load" code means only allows the following code to run once, and the each code basically says "if it's already loaded (as per cached images) then run the load event".
Once the image count is 0 it hides the loading layer.
Without a URL where I can run this through the console I can't be 100% sure it's accurate, so it may need a fiddle about. If you get stuck give us a shout.
Try binding the load event to the images. Keep track of how many have loaded, and remove the loading layer only after they've all loaded. This is untested code but should give you an idea:
success:function(data){
// Set the content
$('#ContentBody').html(data);
// How many images do we have?
var images = $('#ContentBody img'),
leftToLoad = images.size();
// Listen for load event *FOR IMAGES*
images.bind('load', function (){
// Hey look we loaded one, was that the last one?
if(--leftToLoad === 0){
// Yep, we're done
$("#LoadingLayer").hide();
}
});
}
If you'd rather use live than handling in the ajax callback, do this in your $(document).ready callback:
$('#ContentBody img').live('load', function(){...});
Should do the trick.
Cheers
i'm having a problem with dynamically loaded forms - instead of using the action attribute of the newly loaded form, my jquery code is still using the action attribute of the first form loaded. I have the following code:
//generic ajax form handler - calls next page load on success
$('input.next:not(#eligibility)').live("click", function(){
$(".form_container form").validationEngine({
ajaxSubmit: true,
ajaxSubmitFile: $(this).attr('action'),
success : function() {
var url = $('input.next').attr('rel');
ajaxFormStage(url);
},
failure : function() {
}
});
});
But when the next form is loaded, the above code does not pick up the new action attribute. I have tried adding the above code to my callback on successful ajax load (shown below), but this doesn't make any difference.
Can anyone help? Many thanks
function ajaxFormStage(url)
{
var $data = $('#main_body #content');
$.validationEngine.closePrompt('body'); //close any validation messages
$data.fadeOut('fast', function(){
$data.load(url, function(){
$data.animate({
opacity: 'show'
}, 'fast');
');
//generic ajax form handler - calls next page load on success
$('input.next:not(#eligibility)').live("click", function(){
$(".form_container form").validationEngine({
ajaxSubmit: true,
ajaxSubmitFile: $(this).attr('action'),
success : function() {
var url = $('input.next').attr('rel');
ajaxFormStage(url);
},
failure : function() {
}
});
});
});
});
I believe it's happening because using $(this) at this line
ajaxSubmitFile: $(this).attr('action')
you always get the first form's action.
I think you need another selector to get the form that is closest to the clicked input.
Hope you understand what I mean :]