Injecting jQuery where Prototype exsists - noConflict() - prototypejs

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
});

Related

Creating JQuery plugin

I've followed a tutorial to create a Website Menu, this involves JQuery to provide some transitions.
I've got it working as intended, but to prove I understand this new code and framework, and also, to follow development guidelines, I want to move to the Menu code to a plug-in.
Working (not-plug-in) version:
This appears in the .ascx, along with a UL tag, ID = MainMenu
<script type="text/javascript">
NavBarMenu();
</script>
In another file, NavBar.js, the following code is used to associate some JQuery events:
function NavBarMenu() {
$(function() {
var $menu = $('#MainMenu');
var $menu_items = $menu.children('li');
...
Not working version:
Now the plug-in code is created, the NavBarMenu function is called like this from the .ascx:
<script type="text/javascript">
$("#MainMenu").NavBarMenu();
</script>
In NavBar.js I now have:
(function($) {
$.fn.NavBarMenu = function() {
var $menu_items = this.children('li');
However, the $menu_items variable is unpopulated?
Shouldn't this (2nd example) be equivalent to $('#MainMenu') (1st example)
I followed this example, where a JQuery selector is switched for the this pointer. http://learn.jquery.com/plugins/basic-plugin-creation/
Thanks in advance :D
There must be other factor affecting my implementation, I will revisit it.
I have created 2 JSFiddle pages, as part of my investigation and it has proved that the JQuery Selector can be swapped for the this pointer in a plug-in.
Fiddle detailing 1st example: http://jsfiddle.net/rL7zpr15/1/
$('input[type=button]').click( function() {
$( "div" ).css( "background", "green" );
});
Is equivalent to
Fiddle detailing 2nd example: http://jsfiddle.net/hds7advx/
$.fn.greenify = function() {
this.css( "background", "green" );
};
$('input[type=button]').click( function() {
$( "div" ).greenify();
});

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.

Init js after Ajax call

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+

Chosen not working on elements from AJAX call

I have a form which populates div elements based on selections from a select box using an AJAX call.
The content of the populated div is a multiselect box that I want Chosen to apply to. Unfortunately it seems that the 'chzn-select' is not firing, no doubt due to this being pulled in dynamically.
I have added this:
<script type="text/javascript">
$(".chzn-select").chosen();
</script>
To the bottom of the code that is pulled in by AJAX, but it is still not firing. Any ideas on how to make this work as desired?
Solved myself. Will post for future reference. I put the Chosen calls in their own function on my original page that calls the AJAX:
<script type="text/javascript">
function doChosen() {
$(".chzn-select").chosen();
$(".chzn-select-deselect").chosen({allow_single_deselect:true});
}
</script>
And in the AJAX script itself, I added a call to the function after the responseText part:
document.getElementById(div).innerHTML=oXmlHttp.responseText
doChosen();
instead of using chosen(), try change() method. It works on change event.
try:
$(".chzn-select").change(function () {
var str = "";
$("select option:selected").each(function () {
// do your coding here
});
})
.trigger('change');

jquery get. i can't replace content of a div in ie with dynamically loaded html

i have a problem.
just tryin to load some html in a div...everything works fine in every browser except ie.
$('a[rel*=ajax]').click(function(e)
{
ajaxLink = $(this).attr('href')+"?ajax=true";
theDiv = $(this).parents(".post");
$.get(ajaxLink,function(data, textStatus){ $(theDiv).replaceWith(data); });
});
if i try to alert(data) in the .get callback i can read some html, but when i try to "inject" it in theDiv, i get a blank page.
thanks a lot in advance :)
can you try live instead of click and it should work fine.
sometimes in IE the DOM might not be ready
$(a[rel*=ajax).live('click', function() {
});
... but when i try to "inject" it in theDiv, i get a blank page.
You need to cancel the default action of the link; otherwise, the link will be followed. Either add return false to the end of your click handler, or add e.preventDefault(); to it. E.g., this:
$('a[rel*=ajax]').click(function(e)
{
ajaxLink = $(this).attr('href')+"?ajax=true";
theDiv = $(this).parents(".post");
$.get(ajaxLink,function(data, textStatus){ $(theDiv).replaceWith(data); });
return false; // This both prevents the default and stops bubbling
});
or
$('a[rel*=ajax]').click(function(e)
{
ajaxLink = $(this).attr('href')+"?ajax=true";
theDiv = $(this).parents(".post");
$.get(ajaxLink,function(data, textStatus){ $(theDiv).replaceWith(data); });
e.preventDefault(); // This just prevents the default, doesn't stop bubbling
});
Separately:
The quoted code is using ajaxLink and theDiv variables from the parent scope (if they're not declared anywhere, they're implicit global variables). Could it be that you're changing the value of theDiv after the shown code runs but before the ajax call completes? That would mean when the ajax call completes, it's using the new value of theDiv rather than the old one.
In any case, there doesn't seem to be any reason that code should be using variables from the parent scope, I'd recommend making them local to the function:
$('a[rel*=ajax]').click(function(e)
{
var ajaxLink = $(this).attr('href')+"?ajax=true";
// ^-- This is the new bit, the `var`
var theDiv = $(this).parents(".post");
// ^-- And this one
$.get(ajaxLink,function(data, textStatus){ $(theDiv).replaceWith(data); });
});
As a separate issue, are you sure you want replaceWith (which will replace the parent that has the class post) rather than html (which will only replace its contents)?
There's also no reason for calling $ on theDiv again; parents already returns a jQuery object. So:
$('a[rel*=ajax]').click(function(e)
{
var ajaxLink = $(this).attr('href')+"?ajax=true";
var theDiv = $(this).parents(".post");
$.get(ajaxLink,function(data, textStatus){ theDiv.replaceWith(data); });
// ^-- No need for $ here
});
And finally: You might look at the load function, which loads HTML from the server and updates the content of an element with the result.
unfortunately there was a wich was not closed..
and ie seems to be not very flexible with this kind of problem.

Resources