JavaScript function breaking my deep link in jQuery Address crawling - javascript

Strange behavior of JavaScript with jQuery Address (plugin).
I have this code:
var handler = function(data) {
$('#conteudo').hide().html($('#conteudo', data).html()).fadeIn(500);
$.address.title(/>([^<]*)<\/title/.exec(data)[1]);
};
And it works. Perfectly.
Now I change the code:
var handler = function(data) {
$('#conteudo').fadeOut(500, function() {
$('#conteudo').html($('#conteudo', data).html()).fadeIn(500);
});
$.address.title(/>([^<]*)<\/title/.exec(data)[1]);
};
Now the fade out effect works, and after fade in (with new content). Beautiful! But this little change in the way of writing the new content (inside of the new function, after fadeOut) broke my sub-links inside my pages.
Here's a live example:
Access this URL: http://impulse.im/clean/2/
In the top menu, click on 'Contato'.
Now look the href of link 'Rafa' in the loaded content!
http://impulse.im/clean/2?_escaped_fragment_=%2Fcontato%2Frafa.
This is not correct: it should read
http://impulse.im/clean/2/#!/contato/rafa
Again: http://impulse.im/clean/2/ - Click on 'Contato'. Now RELOAD the page.
The link 'Rafa' is now correct.
What this new function (after fadeOut) is doing with the code? Why this function is breaking my link?
Thanks!

The problem is that you are calling the address plugin before the html stored in data is actually placed on the page. It happens because you call the $('#conteudo').html($('#conteudo', data).html()).fadeIn(500) asynchronously as it's called as a callback to the fadeOut method.
Change it this way:
var handler = function(data) {
$('#conteudo').fadeOut(500, function() {
$('#conteudo').html($('#conteudo', data).html()).fadeIn(500);
$.address.title(/>([^<]*)<\/title/.exec(data)[1]);
});
};
This will call your address plugin after the new content was placed in the page.
Before it worked like this.
handler returns data -> content fades out -> you call the address plugin but the content isn't placed on the page yet -> after 500ms you the callback adding the content is called.
Now it'll be like this.
handler returns data -> content fades out -> after 500ms the content is added and the address plugin is called

Related

Prevent Ajax from triggering jQuery script

We got a simple jQuery script on Drupal site that injects a div class with content:
(function($) {
Drupal.behaviors.myHelpText = {
attach: function (context, settings) {
//code starts
//change placeholder text
$('.form-item-quantity').append('<span class="help-block">For orders over 10 call for volume pricing</span>');
$('.help-block').css("flex-basis", "100%");
//code ends
}
};
})(jQuery);
The page has Drupal Commerce and various product attribute fields that gets processed by Ajax every time selecting an attribute. And when doing that our script injects same duplicate line each time on Ajax load/update.
How to avoid that? We just want jQuery code work once on page load.
Only add the element if it doesn't exist, otherwise do nothing.
(function($) {
Drupal.behaviors.myHelpText = {
attach: function (context, settings) {
if (!document.getElementById('help')) {
$('.form-item-quantity').append(
'<span id="help" class="help-block">For orders over 10 call for volume ricing</span>'
);
$('.help-block').css("flex-basis", "100%");
}
}
};
})(jQuery);
You have to understand that drupal.behaviors fire on page load and when ajax returns results. It is designed this way because you may want your code to run again on the ajax results, for example, if you are updating part of the page via ajax and it needs event listeners applied, or a class added.
The context variable is the key here.
on first page load, the context will be the whole window, but when ajax returns the result, the context will just be what is returned by the ajax.
Knowing this, you should be using context in your jquery selectors.
eg.
(function($) {
Drupal.behaviors.myHelpText = {
attach: function (context, settings) {
//code starts
//change placeholder text
$('.form-item-quantity', context).append('<span class="help-block">For orders over 10 call for volume pricing</span>');
$('.help-block', context).css("flex-basis", "100%");
//code ends
}
};
})(jQuery);
For added protection against something processing multiple times, you can use jquery once(), but this is usually not needed if using the context variable in the selector. jQuery once() separate library that must be loaded.
Why don't use jQuery once? My thought — it's a classic approach. A bunch of examples lives in docs on drupal.org
$('.form-item-quantity').once('help-appended').append('<span class="help-block">For orders over 10 call for volume pricing</span>');
And I'm not sure you need to apply styles via js. A css file is a better place for it. And jquery once should be available in your environment. That's it.

have function work after div content is updated

I have a div with content in it. When the page loads, this function works fine. But when I update the div with content using javascript, this function fails to work.
Can anyone help me figure out what I should do to make this work?
$("textarea.indent").each(function(){
var indentWidth = $(this).siblings('[class=indent]').width();
if(indentWidth != null)
$(this).css('text-indent', (indentWidth+5)+'px');
});
Are you loading $("textarea.indent") dynamicly?
Jquery binds all events on document ready ie. when the page loads. That means that elements added after the page is done loading won't get bound to that event. To fix this you need to dynamicly bind your events as well. Like this..
$.ajax{
...
//Some ajax call
success: function(){
//Bind event
$("textarea.indent").each(function(){
var indentWidth = $(this).siblings('[class=indent]').width();
if(indentWidth != null)
$(this).css('text-indent', (indentWidth+5)+'px');
});
}
}
It doesn't have to be a ajax request thats add the elements, but my point still stands.

Infinite Scroll + Swipe.js

Background:
I'm making a portfolio site utilising both Swipe.js and Infinite Ajax Scroll (JQ).
Problem:
When the content from extra pages is loaded into the current page, it is not processed by the already-loaded Swipe.js script. This means that the new content doesn't have it's mark-up changed (needed for the swipe functionality to work).
I think I need to get the Swipe.js script to fire after each page re-load. Would that fix it? Please explain this to me like I'm an 8yr old. JS is not a strong suit...
Demo:
http://hatchcreative.co.nz/tomo
You can see that as the page loads new content, the buttons on either side of the sliders no longer work.
Yes you're right, after the images are loaded you have to create a new Swipe instance on these new elements (as they weren't there at the beginning, when the page was loaded).
Based on the docs of infinite scroll you can use onRenderComplete.
So you had your jQuery.ias constructor like this:
jQuery.ias({
// ... your settings...
onRenderComplete: function(items) {
$(items).each(function(index, element) {
new Swipe(element);
});
}
});
This should work this way somehow, but I am not exactly sure; I haven't worked with these libraries yet.
Edit:
After some more inspection of your code, I saw you had some inline click handler like: onclick='two.prev();return false;'.
You need to remove this and add your onclick handle in the same onRenderComplete function.
onRenderComplete: function(items) {
var swipe;
$(items).each(function(index, element) {
swipe = new Swipe(element);
});
// find tags with the class 'forward' inside the current element and add the handler
$(element).find('.forward').on('click', function() {
swipe.next();
});
// ... also for previous
}
By the way: Usually you should provide a jsFiddle with your important code parts, so it's easier for us to get the problem, and the question is not getting obsolote when the linked page changes.

How can i access a css attribute from a .load()'ed page?

I want to load an entire website in to my website. I have been able to do this with $(#preview).load("index.php") into my <div id="preview"></div>. What i have troubles accessing here is the background image which is a property of body. I tried making a div tag with a background-image attribute but when i removed the image from my body tag it didnt behave like i wanted to (was not filling the entire space).
My question is this. How can i access something from index.php that can let me either preview the site correctly or copy the attribute from somewhere into the preview background-image attribute?
my code now looks like this, after some extensive try-and-error (more like error-and-error and its getting more and more messy)
$(document).ready(
function() {
$("#check").click(
function() {
var bgd;
$("#preview").load("index.php",
function () {
bgd = $("#bg").css("background-image");
}
);
$("#preview").style.backgroundImage(bgd);
}
);
}
);
Where bg is the id of the div which works as a "substitute" body tag in index.php (aka with the same attributes as body)
Im either far from it, or ridiculously close. Thanks for every piece of advice i can get.
$(document).ready(
function() {
$("#check").click(
function() {
$.ajax({
url: 'index.php',
success: function(data){
data = $('data').html();
$('#preview').html(data);
bg = $('data').find('#bg').css('background-image');
$('#preview').css('background-image',bg);
}
});
}
);
}
);
Two problems here. One with how you're accessing style properties and the other has to do with the asynchronous behavior of load().
Style problem
You'll need to use $("#preview").css('background-image',bgd). The first method you were using could still be kept around by accessing the non-jQuery wrapped element, like so:
$("#preview")[0].style.backgroundImage(bgd);
Async problem
The second issue is that .load() is an asynchronous call that returns immediately. The next line of code gets executed and bdg is (most likely) still undefined. Then when load() completes, the success handler gets executed and bdg gets set to the background of the loaded page, but it's too late!
Moving the $("#preview").css('background-image',bgd) into the success handler will rectify that problem:
$(document).ready(
function() {
$("#check").click(
function() {
var bgd;
$("#preview").load("index.php",
function () {
bgd = $("#bg").css("background-image");
$("#preview").css('background-image',bgd);
}
);
}
);
}
);

Wrapping all AJAX calls jQuery

I populate many parts of my website using
$("#theDivToPopulate").load("/some/api/call.php", callBackToBindClickEventsToNewDiv);
Where /some/api/call.php returns a built list, div, or some other HTML structure to place directly into my target div. The internet has been running slow lately and I've noticed that the time between a button click (which kicks off these API calls) and the div populating is several seconds. Is there an easy way to globally wrap all the load calls so that a div containing "Loading..." is displayed before the call is even made and hidden once the API call is complete.
I can not simply put the code to hide the div into the callBackToBindClickEventsToNewDiv as some load events have different call backs. I would have to copy the code into each function which is ugly and defeats the purpose. I want the flow of any .load to go as follows:
1) dispplayLoadingDiv()
2) Execute API call
3) Hide loading div
4) do callback function.
The loading div must be hidden first as the callback contains some animations to bring the newly loaded div in nicely.
EDIT:
Expanding on jacktheripper's answer:
var ajaxFlag;
$(document).ajaxStart(function(){
ajaxFlag = true;
setTimeout(function (e) {
if(ajaxFlag) {
hideAllDivs();
enableDivs(['loading']);
}
}, 500);
}).ajaxStop(function(){
ajaxFlag = false;
var load = $("#loading");
load.css('visibility','hidden');
load.css('display','none');
load.data('isOn',false);
});
This way loading is only displayed if the page takes more than 500 MS to load. I found the loading flying in and out real fast made things kind of choppy for fast page loads.
Use the following jQuery:
$(document).ajaxStart(function(){
$('#loader').show();
}).ajaxStop(function(){
$('#loader').hide();
});
Where you have an element called #loader that contains what you want to show when an AJAX request is being performed. It could be a span with text, an image (eg a gif), or anything similar. The element should be initially set to display: none
You do not even need to call the function anywhere else.
Try this
$("#someButtonId").click(function(e){
e.preventDefault();
$("#theDivToPopulate").html("Loading...");
$.get("/some/api/call.php",function(data){
$("#theDivToPopulate").fadeOut(100,function(){
$("#theDivToPopulate").html(data).fadeIn(100,function(){
//Do your last call back after showing the content
});
});
});
});

Categories

Resources