Init js after Ajax call - ajax

I have a small Javascript used to swap images using a href links. Everything is OK except when I call those a ref inside the page using Ajax. I need to find a way to initialize again the script after calling new links.
Here is the JS:
<script type="text/javascript">
$('a.thumbnail').click(function(){
var src = $(this).attr('href');
if (src != $('img#largeImg').attr('src').replace(/\?(.*)/,'')){
$('img#largeImg').stop().animate({
opacity: '0'
}, function(){
$(this).attr('src', src+'?'+Math.floor(Math.random()*(10*100)));
}).load(function(){
$(this).stop().animate({
opacity: '1'
});
});
}
return false;
});
Thank you

You need to delegate the events.
Turn this:
$('a.thumbnail').click(function(){ /*...*/ });
into this:
$('#someWrapperContainer').on('click', 'a.thumbnail', function(){ /*...*/ });
Make sure you're using jQuery 1.7+

Related

Twitter Bootstrap 3 dropdown menu disappears when used with prototype.js

I have an issue when using bootstrap 3 & prototype.js together on a magento website.
Basically if you click on the dropdown menu (Our Products) & then click on the background, the dropdown menu (Our Products) disappears (prototype.js adds "display: none;" to the li).
Here is a demo of the issue:
http://ridge.mydevelopmentserver.com/contact.html
You can see that the dropdown menu works like it should without including prototype.js on the page at the link below:
http://ridge.mydevelopmentserver.com/
Has anyone else ran into this issue before or have a possible solution for the conflict?
EASY FIX:
Just replace Magento's prototype.js file with this bootstrap friendly one:
https://raw.github.com/zikula/core/079df47e7c1f536a0d9eea2993ae19768e1f0554/src/javascript/ajax/original_uncompressed/prototype.js
You can see the changes made in the prototype.js file to fix the bootstrap issue here:
https://github.com/zikula/core/commit/079df47e7c1f536a0d9eea2993ae19768e1f0554
NOTE: JQuery must be include in your magento skin before prototype.js.. Example:
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/prototype/prototype.js"></script>
<script type="text/javascript" src="/js/lib/ccard.js"></script>
<script type="text/javascript" src="/js/prototype/validation.js"></script>
<script type="text/javascript" src="/js/scriptaculous/builder.js"></script>
<script type="text/javascript" src="/js/scriptaculous/effects.js"></script>
<script type="text/javascript" src="/js/scriptaculous/dragdrop.js"></script>
<script type="text/javascript" src="/js/scriptaculous/controls.js"></script>
<script type="text/javascript" src="/js/scriptaculous/slider.js"></script>
<script type="text/javascript" src="/js/varien/js.js"></script>
<script type="text/javascript" src="/js/varien/form.js"></script>
<script type="text/javascript" src="/js/varien/menu.js"></script>
<script type="text/javascript" src="/js/mage/translate.js"></script>
<script type="text/javascript" src="/js/mage/cookies.js"></script>
<script type="text/javascript" src="/js/mage/captcha.js"></script>
I've also used code from here: http://kk-medienreich.at/techblog/magento-bootstrap-integration-mit-prototype-framework but without a need to modify any source. Just put code below somewhere after prototype and jquery includes:
(function() {
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('*');
jQuery.each(['hide.bs.dropdown',
'hide.bs.collapse',
'hide.bs.modal',
'hide.bs.tooltip',
'hide.bs.popover',
'hide.bs.tab'], function(index, eventName) {
all.on(eventName, function( event ) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function(element) {
if(isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
})();
Late to the party, but found this github issue which links to this informational page which links to this jsfiddle which works really nicely. It doesn't patch on every jQuery selector and is, I think, the nicest fix by far. Copying the code here to help future peoples:
jQuery.noConflict();
if (Prototype.BrowserFeatures.ElementExtensions) {
var pluginsToDisable = ['collapse', 'dropdown', 'modal', 'tooltip', 'popover'];
var disablePrototypeJS = function (method, pluginsToDisable) {
var handler = function (event) {
event.target[method] = undefined;
setTimeout(function () {
delete event.target[method];
}, 0);
};
pluginsToDisable.each(function (plugin) {
jQuery(window).on(method + '.bs.' + plugin, handler);
});
};
disablePrototypeJS('show', pluginsToDisable);
disablePrototypeJS('hide', pluginsToDisable);
}
Using the * selector with jQuery is not advised. This takes every DOM object on the page and puts it in the variable.
I would advice to select the elements that use a Bootstrap component specific. Solution below only uses the dropdown component:
(function() {
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('.dropdown');
jQuery.each(['hide.bs.dropdown'], function(index, eventName) {
all.on(eventName, function( event ) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function(element) {
if(isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
})();
Very late to the party: if you don't feel like having extra scripts running, you can add a simple CSS override to prevent it from getting hidden.
.dropdown {
display: inherit !important;
}
Generally the use of !important in CSS is advised against, but I think this counts as an acceptable use in my opinion.
see http://kk-medienreich.at/techblog/magento-bootstrap-integration-mit-prototype-framework/.
It's quite an easy fix to validate the namespace of the element clicked.
Add a validation function to prototype.js:
and after that, validate the namespace before the element will be hidden:
hide: function(element) {
element = $(element);
if(!isBootstrapEvent)
{
element.style.display = 'none';
}
return element;
},
Replacing Magento's prototype.js file with the bootstrap friendly version suggested by MWD is throwing an error that prevents saving configurable products:
Uncaught TypeError: Object [object Array] has no method 'gsub' prototype.js:5826
(Running Magento Magento 1.7.0.2)
evgeny.myasishchev solution worked great.
(function() {
var isBootstrapEvent = false;
if (window.jQuery) {
var all = jQuery('*');
jQuery.each(['hide.bs.dropdown',
'hide.bs.collapse',
'hide.bs.modal',
'hide.bs.tooltip'], function(index, eventName) {
all.on(eventName, function( event ) {
isBootstrapEvent = true;
});
});
}
var originalHide = Element.hide;
Element.addMethods({
hide: function(element) {
if(isBootstrapEvent) {
isBootstrapEvent = false;
return element;
}
return originalHide(element);
}
});
})();
This answer helped me to get rid of bootstrap and prototype conflict issue.
As #GeekNum88 describe the matter,
PrototypeJS adds methods to the Element prototype so when jQuery tries
to trigger the hide() method on an element it is actually firing the
PrototypeJS hide() method, which is equivalent to the jQuery
hide() method and sets the style of the element to display:none;
As you suggest in the question itself either you can use bootstrap friendly prototype or else you can simply comment out few lines in bootstrap as bellow,
inside the Tooltip.prototype.hide function
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
I realise that this is a pretty old post by now, but an answer that no-one else seems to have mentioned is simply "modify jQuery". You just need a couple of extra checks in jQuery's trigger function which can be found around line 332.
Extend this if statement:
// Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
... to say this:
// Call a native DOM method on the target with the same name name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
// Check for global Element variable (PrototypeJS) and ensure we're not triggering one of its methods.
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) &&
( !window.Element || !jQuery.isFunction( window.Element[ type ] ) ) ) {
"#6170" only seems to be mentioned in jQuery once so you can do a quick check for that string if you're working with a compiled (complete) jQuery library.

hash in url to deep linking with ajax

I've this code to load content in a div #target with some animation. Works fine but i don't know how implement code to change link and url with #hash!
How can I do this?
the code:
$(document).ready(function(){
$("#target").addClass('hide');
$('.ajaxtrigger').click(function() {
var pagina = $(this).attr('href');
if ($('#target').is(':visible')) {
}
$("#target").removeClass('animated show page fadeInRightBig').load(pagina,
function() {
$("#target").delay(10).transition({ opacity: 1 })
.addClass('animated show page fadeInRightBig');
}
);
return false;
});
});
Try to use any javascript router. For example, router.js.
Modify you code like this(I didn't check if this code work, but I think idea should be clear):
$(document).ready(function(){
var router = new Router();
//Define route for your link
router.route('/loadpath/:href', function(href) {
console.log(href);
if ($('#target').is(':visible')) {
$("#target").removeClass('animated show page fadeInRightBig').load(href,
function() {
$("#target").delay(10).transition({ opacity: 1 })
.addClass('animated show page fadeInRightBig');
}
);
}
});
router.route('', function(){ console.log("default route")});
$("#target").addClass('hide');
// Instead of loading content in click handler,
// we just go to the url from href attribute with our custom prefix ('/loadpath/').
// Router will do rest of job for us. It will trigger an event when url hash is
// changes and will call our handler, that will load contents
// from appropriate url.
$('.ajaxtrigger').click(function() {
router.navigate('/loadpath/' + $(this).attr('href'));
return false;
});
});

How to resolve jQuery conflict

I have a few lines of jQuery codes that load external pages when the links are clicked.
$(document).ready(function() {
$(".link").click(function (e) {
e.preventDefault();
var $urlToLoad = $(this).attr('href');
$("#loadarea").load($urlToLoad, function(data){
$("#loading").fadeIn('fast').fadeOut('fast');
$("#loadarea").hide().fadeIn('slow');
return false;
});
});
});
This works fine. However, when I add this one single line of additional code, which is essential on this page, "$ is undefined" error shows up.
I've tried every single technique at http://api.jquery.com/jQuery.noConflict/ but, I can't resolve the conflict.
function goto(id, t){
$(".contentbox-wrapper").animate({"left": -($(id).position().left)}, 600);
$('#slide a').removeClass('active');
$(t).addClass('active');
}
I've tried var jq=jQuery.noConflict(); to replace $ but this doesn't solve the problem.
I guess I do not understand enough of jQuery to resolve this conflict and I would really appreciate anyone who can explain what is going on so that I can learn from this.
So all together, it looks like this:
<script>
$(document).ready(function() {
$(".link").click(function (e) {
e.preventDefault();
var $urlToLoad = $(this).attr('href');
$("#loadarea").load($urlToLoad, function(data){
$("#loading").fadeIn('fast').fadeOut('fast');
$("#loadarea").hide().fadeIn('slow');
return false;
});
});
});
function goto(id, t){
$(".contentbox-wrapper").animate({"left": -($(id).position().left)}, 600);
$('#slide a').removeClass('active');
$(t).addClass('active');
}
</script>
Then I have one inline code to fire the script.
(a class="active" href="#" onClick="goto('#kr', this); return false">test
Strange thing is, that even with the error, it fires on second click.
/////////////////////////////////////
The conflict/error was resolved by converting the inline javascript.
Thanks to Huangism below.
I would just rewrite the anchor from
<a class="active" href="#" onClick="goto('#kr', this); return false">test</a>
To
<a class="active" href="#kr">test</a>
For the jquery
$('.active').on('click', function() {
var $this = $(this);
var id = $this.attr('href');
$(".contentbox-wrapper").animate({"left": -($(id).position().left)}, 600);
$('#slide a').removeClass('active');
$this.addClass('active');
return false;
});
$(document).ready(function() {
});
Should be rewritten as:
jQuery(function($) {
});
That might help you out here.
It's probably to do with the order that you're including your js files in, make sure that jquery is the first loaded file.

jQuery Mobile transitions and AJAX Polling on a MasterPage

I am trying to use AJAX polling with jQuery to update a span element on a razor MasterPage in ASP.NET MVC3. The page uses the jQuery Mobile 1.0 framework that adorns simple view changes (like navigating from /home to /about) with some sort of "transition" animation.
This is the Javascript code that does the polling, while the "unreadBubble" span is located in the body - both are defined in the MasterPage!
<script type="text/javascript">
$(document).bind("pageinit", function poll() {
setTimeout(function () {
$.ajax({ url: "/Notification/GetUnreadNotificationsCount",
dataType: "json",
success: function (data) {
$('#unreadBubble').text(data.UnreadCount);
poll();
}
});
}, 1000);
});
So, imagine I have a HomeController and a NotificationController that both use the MasterPage and provide an Index view. The AJAX polling works on both views and updates the span every second as expected. As soon as I navigate from one view to another though, the span gets re-initialized with its default value from the MasterPage (empty) and doesn't update anymore. Interestingly the async GetUnreadNotificationsCount method is still called on the NotificationsController repeatedly - the span just doesn't update. I also tried to alert the span tag in JS and it wasn't null or something.
According to the documentation, jQuery Mobile also loads new pages with AJAX to insert this fancy "SWOOSH" transition animation. This seems to somehow disturb the JS/DOM initialization.
Do you have any idea how to resolve this? Should I bind to another event or can I somehow force the span tag to update?
Solution: It was a caching problem! The following did the trick:
Add class="command-no-cache" to your page div add the following JavaScript to the MasterPage:
$(":jqmData(role=page)").live('pagehide', function (event, ui) {
if ($(this).children("div[data-role*='content']").is(".command-no-cache"))
$(this).remove();
});
I would use the pagebeforeshow to actually bind the event, and pagehide to remove the event.
Did you try that instead of initializing only once in the pageinit event?
UPDATE: some code for example,
<script type="text/javascript">
var timer = null;
$(":jqmData(role=page)").bind("pagebeforeshow", function() {
timer = setTimeout(function() {
$.ajax({ url: "/Notification/GetUnreadNotificationsCount",
dataType: "json",
success: function (data) {
$('#unreadBubble').text(data.UnreadCount);
}
});
}, 1000);
});
$(":jqmData(role=page)").bind("pagehide", function() {
if (timer != null){
clearTimeout(timer);
timer = null;
}
});
</script>
Also corrected some other ""mistypes" along the way, have a look and compare to your code!
Hope this helps

Injecting jQuery where Prototype exsists - noConflict()

I'm using webkit only. I need to inject jQuery into a page that already has prototype loaded. I'm using this code to load jQuery. (you can try in console)
var s = document.createElement('script');
s.setAttribute('src', 'http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.js');
s.setAttribute('type', 'text/javascript');
document.getElementsByTagName('head')[0].appendChild(s);
I get an error with just the code above.
How can I use noConflict() on load. If I put the following code after injecting the jquery script, I still get an error.
$(document).ready(function() {
jQuery.noConflict();
// my thing here
});
This also throw an error:
jQuery.noConflict();
$(document).ready(function() {
// my thing here
});
EDIT: Because you're loading the script from another script, you should put the jQuery code you need to run in a callback to a load event for your script:
var s = document.createElement('script');
s.setAttribute('src', 'http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.js');
s.setAttribute('type', 'text/javascript');
document.getElementsByTagName('head')[0].appendChild(s);
// Place your code in an onload handler for the jQuery you're loading
s.onload = function() {
jQuery.noConflict(); // release jQuery's hold on "$"
jQuery(document).ready(function( $ ) {
alert( $.fn.jquery );
});
};
Another solution would be to not use this method of loading jQuery. Just hardcode your <script> element, and the code will run in the expected synchronous manner:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.js" type="text/javascript"></script>
<script type="text/javascript">
jQuery.noConflict(); // release jQuery's hold on "$"
// do this with ready() -------v------ and the "$" will be available inside
jQuery(document).ready(function( $ ) {
// $ is safe for jQuery inside this .ready() callback
alert( $.fn.jquery );
});
</script>
Original answer:
Do this:
var s = document.createElement('script');
s.setAttribute('src', 'http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.js');
s.setAttribute('type', 'text/javascript');
document.getElementsByTagName('head')[0].appendChild(s);
jQuery.noConflict(); // release jQuery's hold on "$"
// do this with ready() -------v------ and the "$" will be available inside
jQuery(document).ready(function( $ ) {
// $ is safe for jQuery inside this .ready() callback
alert( $.fn.jquery );
});
Try
var $j = jQuery.noConflict();
$j(document).ready(function() {
  // my thing here
});
You can then use $j for any jquery $
$ is the alias/shortcut for jQuery (as well as prototype). NoConflict basically releases control of the $ shortcut, so that once called, the other library has control of it. Try this:
jQuery(document).ready(function() {
// my thing here
});
Here, you use $ first and then use jQuery.noConflict(), the problem is that you've (wrongly) assumed $ is jQuery before you've set up the no conflict:
$(document).ready(function() {
jQuery.noConflict();
// my thing here
});
Here, you've done the opposite. You've done the no conflict bit first, good, but then continued to use $ to access jQuery, which will no longer work (as a direct result of the noConflict() call):
jQuery.noConflict();
$(document).ready(function() {
// my thing here
});
Combining your two efforts you end up with the following. I've also added a $ to the .ready line so that inside the ready function you can still use $ as your jQuery reference.
jQuery.noConflict(); // stops $ being associated with jQuery
jQuery(document).ready(function($) { // use long-hand jQuery object reference to call `ready()`
// my thing here
});

Resources