Drupal 7 override jquery js file in custom theme - javascript

Is it possible to rewrite/override a default Drupal 7.26 jquery used in custom template scripts variable ?
I mean one of the js files:
<script type="text/javascript" src="http://drupal.dev/misc/jquery.js?v=1.4.4"></script>
by one which comes by custom theme ?
I tried this in sites/all/MYTPL/template.php but it doesn't work:
$scripts['misc/jquery.js']['data'] = $base_theme . '/js/packages/jquery-2.1.0.min.js';
I would like to know if someone managed it without using any modules such as jQuery Update ?
=== UPDATED ===
Actually I solved it by help of accepted answer with some modifications based on #cojomojo 's answer:
function THEMEname_js_alter(&$javascript) {
// Swap out jQuery to use an updated version of the library.
$javascript['misc/jquery.js']['data'] = drupal_get_path('theme', 'THEMEname') . '/js/packages/jquery-2.1.0.min.js';
}

Use this code inside your theme's template.php file, in hook_js_alter.
function [YOUR_THEME]_js_alter(&$js)
{
$jqKey = "my-new-jquery"; // a key for new jquery file entry
$js[$jqKey] = $js['misc/jquery.js']; // copy the default jquery settings, so you don't have to re-type them.
$js[$jqKey]['data'] = "https://code.jquery.com/jquery-2.1.0.min.js"; // the path for new jquery file.
$js[$jqKey]['version'] = '2.1.0'; // your jquery version.
unset($js['misc/jquery.js']); // delete drupal's default jquery file.
}

So you have a few options to do this. Here they are:
You can use hook_js_alter like so:
<?php
function hook_js_alter(&$javascript) {
// Swap out jQuery to use an updated version of the library.
$javascript['misc/jquery.js']['data'] = drupal_get_path('module', 'jquery_update') . '/jquery.js';
}
?>
Or in order to avoid breakage like 2pha mentioned you can run two versions of jquery side by side. jQuery already has the functionality to run along side a different version of jQuery (or, really, along side any other JavaScript library that uses the $ symbol as a function or variable). This is the noConflict() function. You can view the API page here: http://api.jquery.com/jQuery.noConflict/
To use this functionality within your project, simply tell the browser about your new jQuery library and that you'll be referencing it via your custom noConflict identifier. Here is an example of how you would implement this in a custom theme.
MyTheme/page.tpl.php
<head>
<title><?php print $head_title; ?></title>
<?php print $head; ?>
<?php print $styles; ?>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript">
var $jq = jQuery.noConflict();
</script>
<?php print $scripts; ?>
</head>
Another possible method is to swap jQuery with the preprocess page like so:
<?php
function yourModuleOrThemeName_preprocess_page(&$variables) {
// exclude backend pages to avoid core js not working anymore
// you could also just use a backend theme to avoid this
if (arg(0) != 'admin' || !(arg(1) == 'add' && arg(2) == 'edit') || arg(0) != 'panels' || arg(0) != 'ctools') {
$scripts = drupal_add_js();
$new_jquery = array(drupal_get_path('theme', 'YOURTHEME') . '/js/jquery-1.7.1.min.js' => $scripts['core']['misc/jquery.js']);
$scripts['core'] = array_merge($new_jquery, $scripts['core']);
unset($scripts['core']['misc/jquery.js']);
$variables['scripts'] = drupal_get_js('header', $scripts);
}
}
?>
To me the best method would be to either run two version of jQuery side by side (the version that Drupal 7 uses by default and whatever your theme requires) or swapping jQuery with the preprocess page. The hook_js_alter has potential for breaking anything that requires the different version of jQuery. All the potential ways to do what you ask are here: https://drupal.org/node/1058168

Maybe use the hook hook_js_alter.
Keep in mind though that other modules will depend on jQuery and if they use something that has changed between versions, you may encounter problems.

Related

React - Using external js from CDN

Sorry, I put this again since the old post got merged into some post that doesn't relate to my question ... I'm new to React and trying to convert a php website into react components. However, in old website there are some function in pure jquery and from CDSNJS. The external javascripts function are not binding well with my component and I cannot figure out how to. Please can anyone give me some advice.
Case 1:
I got a an external function like this:
;(function ($) {
/* Global variables */
var prty, flickr;
$.fn.flickrGallery = function(flickrID) {
//Defaults settings
var opts = $.extend({}, {
Key:prty.settings["Key"],
Secret:prty.settings["Secret"],
User:prty.settings["User"],
PhotoSet:flickrID,
Speed:400,
navigation:1,
keyboard:1,numberEl:1 });
//Setup
prty.Setup($(this), opts);
}; //End FN
prty = {
.... Internal code
};
})(jQuery);
And this is my component's code:
async componentDidMount() {
//$('#gallery').flickrGallery(2); // Not work
//const el = ReactDOM.findDOMNode(this.display); Not work
//$(el).vectorMap({map: 'world_mill_en'});
//$(el).flickrGallery(2);
//const el = ReactDOM.findDOMNode(this.domRef); Not work
//window.$('#addSupModal').modal('show')
//$(el).flickrGallery(2);
window.$(this.productIntro).flickrGallery(2); Not work
}
Every time I run, an error like this appears:
Unhandled Rejection (TypeError): window.$(...).flickrGallery is not a function
Case 2:
Beside the case above, I'm also using a lib from CDNJS
<!-- jQuery 1.8 or later, 33 KB -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!-- Fotorama from CDNJS, 19 KB -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/fotorama/4.6.4/fotorama.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/fotorama/4.6.4/fotorama.js"></script>
I have tried including these links into index.html but the same error as above happens when I run ... Please help
Try to access the flickrGallery function via window.jQuery instead of window.$.
The plugin add the flickrGallery function to jQuery. In most of the time, jQuery should be the same as $. However, in some cases, multiple version of jQuery are loaded and jQuery may no longer be equals to $.
The following suggestions were told to be not solving the problem. I will keep them below in case someone find it useful.
It looks like your react component script is executed and rendered before the external scripts is being executed. There are many causes to it and one of the reasons is the ordering of your script tags.
In the document head, make sure the ordering of your script tag is as follow.
<script src="path/to/external/library.js"></script>
<script src="path/to/your/react/script.js"></script>
These script tags do not have the defer nor async attribute, suggesting they should be downloaded, parsed and executed in order. The external script is executed first.
In other cases, where there are defer, async, etc. attributes are in the script tag, you can understand the order of execution by reading this cheat sheet.
If you are using plugin which adds its functionality to some global variable, your bundler may tree-shake it away because it does not detect any usage of its exports and thinks the module is not needed. In this case, you have to add side effect import (Doc).
In your entry module, add this at the very top:
import 'some-jquery-plugin'

Include a javascript file only once in Joomla

now, this question has been asked and answered successfully many times, yet none of the things i try work.
I have tried head.js & require.js libraries
I have also tried
if (!window.unique_name) {
unique_name = true;
//code here..
}
none of which I can get to work (the global variable is always undefined)
the script I am trying to include runs something like this:
//clock.js
clockyTick = function() {
//my code here
}
setInterval(clockyTick, 1000);
the apps that call this script, standalone, work fine.
only when both apps are included on the same page (via calls to PHP require()) they break.
Here is the cause of the problems (I think):
I am building custom web apps on a (Joomla) site and have the requirement of displaying two of my apps on the same page.
Both apps need the same .js file to operate correctly, which works fine when they run standalone, but as soon as both apps are running on the same page (in the admin section) the scripts conflict and stop each other from working
(the script in question is a dynamic clock script that grabs the specialised contents of a div and modifies it to something else)
I think the reason I cannot get aforementioned libraries to work, is the fact that they also are being included twice on the admin page.
is there any way around this, or do I have to bite the bullet and integrate a library into the main Joomla template? (meaning the library is uselessly loaded on every single page, yet only used on 3 of hundreds)
jQuery is also required, separately, on each app..but thankfully I am able to use noConflict to avoid problems there (not ideal)
The joomla way would be to instantiate the document inside your module and unset only the conflicting script as described in this question here just before you load the module's script:
1) get an instance if the document object and remove the js files (you
could do that in a plugin) :
<?php
//get the array containing all the script declarations
$document = JFactory::getDocument();
$headData = $document->getHeadData();
$scripts = $headData['scripts'];
//remove your script, i.e. mootools
unset($scripts['/media/system/js/mootools-core.js']);
unset($scripts['/media/system/js/mootools-more.js']);
$headData['scripts'] = $scripts;
$document->setHeadData($headData);
?>
Or in your case, I think you could try the dirty solution below inside your js files:
//1st module script
var unique_name;
if (unique_name == false || unique_name == null) {
unique_name = true;
//code here..
alert("Included 1st script");
}else{
//do nothing
alert("Not included 1st script")
}
//2nd module script
var unique_name;
if (unique_name == false || unique_name == null) {
unique_name = true;
//code here..
alert("Included 2nd script");
}else{
//do nothing
alert("Not included 2nd script")
}
Here is a DEMO
If you are having conflicts with PHP require(), you can try require_once(). However, as mentioned, that’s not the Joomla way of doing things.

Simplest way to selectively activate generic jQuery/javascript modules for a specific page?

It's oft recommended optimization to consolidate your javascript into fewer parts and reduce the number http requests. On a smaller site you could easily arrive at structure like:
/js/jQuery-xx.js
/js/plugins.js
/js/app.js
Even in a modest site (with just some jQuery polish) that app.js can get pretty heavy. For organization it's easy to break apart the implementations into logcal bits:
..
/js/app-global.js
/js/app-contact.js
/js/app-products.js
/js/app-splash.js
Now as separate files it's trivial to require them for a specific context, though their individual sizes are quite small, around 1–3 kb a piece, so it occurs to me it'd be pretty smart to recombine them back into a single file simply served as app.js.
But it occurs to me that in app.js file with about 20 or so jQuery selectors and misc javascript that goes unused on most pages. Seems like wasted CPU cycles to have these jQuery selectors firing when they target elements that don't exist in most contexts.
Which is the more pressing optimization: • reducing http requests? • reducing unused jQuery selectors?
I'm using Chrome's Audit tool to profile and it recommends fewer network request for javascript files. Any guidelines for measuring bloat in jQuery.ready(…);?
Update
Weighing the evidence presented the simplest thing to do is to serve the implementation code as a single file and then activate different modules using a routing system. In my case I just hooked into the simple php router that I've already built:
root.php
<script type="text/javascript">
var myApp = {
modules: {}
bootstrap: function()
{
<?php
if ( $pageId == 'contact' )
echo 'myApp.modules.contact();';
if ( $pageId == 'index' )
echo 'myApp.modules.splash();';
if ( preg_match( '~^help/~iu', $pageid ) )
echo 'myApp.modules.faq();';
?>
}
}
</script>
<script type="text/javascript" src="/js/jquery1.9.1.js"></script>
<script type="text/javascript" src="/js/jquery-plugins.js"></script>
<script type="text/javascript" src="/js/myApp.js"></script>
myApp.js
$(document).ready(function()
{
myApp.bootstrap();
//… global jQuery stuff …
});
// Define modules
myApp.modules.splash = function() {
$('#splash').click(
//…etc…
);
}
myApp.modules.faq = function() {
$('.faq').show(
//…etc…
);
}
myApp.modules.contact = function() {
$('.contact').bind(
//…etc…
);
}
Serving your static content in as few files as possible is always the recommendation. If you're worried about executing on content that doesn't exist then you should organize your code in such a way that it doesn't all automatically fire on page load, but fires when you tell it to. That way you only execute the code you want to on any given page. The easiest way would be to wrap each pages content in it's own function:
window.aboutPage = function() {
$('').css(); //some stuff
}
then at the bottom of the about page, after your load all your external scripts fire off the appropriate function
<script>$(window.aboutPage) //fire aobutPage stuff on ready</script>
There are frameworks that can accomplish this modularization of code also
If you're using modules (I'm guessing RequireJS?) you can avoid putting a bunch of functions on the global/window object by implementing a simple "router". Have your main app module define a hash of "routes" something like:
var routes = {
'/contact': contact,
'/products': products,
'/splash': splash
};
Where each of contact, products, and splash, is the function you want to call "on DOM ready". Then elsewhere in your same app module pass the appropriate function as jQuery's ready handler something like:
$(routes[window.location.pathname])
Note that:
You might need to do something fancy to make sure you're only using the part of the URL/path that you really want.
You could drop the jQuery dependency (if you want) and use RequireJS's domReady. They apparently work about the same.

Managing third party javascript libraries with bundled jQuery

I am working on a website globalization project, which involves (us the vendor) asking our clients to insert a script tag on their home/origin site. The script tag is necessary for helping our clients go global, and part of the solution embodies a UI that gets triggered based on certain end user criteria.
The UI is built through the help of jQuery, which we really can't expect our clients to insert on their pages, not to mention version mismatches will be hard to resolve. Therefore, our third party library downloads its own jQuery version, albeit namespaced differently to avoid any conflicts.
However, such a mechanism requires us to rename all instances of jQuery to something that will help us avoid name clashes with another jQuery instance (if present), and makes the our mangled jQuery (MY_Query in examples below) very hard to manage, let alone upgrade.
For example
jQuery = window.jQuery = window.$ = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
},
.
.
.
jQuery.fn = jQuery.prototype = ...
becomes
MY_JQuery = window.MY_JQuery = window.MY_Q = function( selector, context ) {
// The MY_JQuery object is actually just the init constructor 'enhanced'
return new MY_JQuery.fn.init( selector, context );
},
.
.
.
MP_JQuery.fn = MP_JQuery.prototype = ...
In an ideal world, both us and the client would have a single version of jQuery on the site, and we both would use it to our advantage. But that would mean an upgrade of jQuery would require heavy testing on both sides (while the mangled jQuery version is contained) and that any plugin wanted would require the client to add appropriate script tags to their site, spurring a political debate between the two parties on what versions win.
So, can I manage our jQuery version (with plugins) on a client site without having to rename all instances of jQuery with something like MY_Query with the constraints mentioned above?
Why not check to see if they have jQuery already included on the page and if not dynamically load it? If you know the base level jQuery needed you can check for that like this:
if( !jQuery || !jQuery.fn.jquery === "1.4.4"){
var url = "http://code.jquery.com/jquery-1.4.4.js";
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = url;
document.body.appendChild(script);
}
You'd probably want to improve the version detection to make sure that it doesn't have a version after 1.4.4, but I'm sure you could write the code for that yourself ;-)
==== Edit based on feedback
So you need to maintain multiple versions of jquery on the page. Have you tried something like this:
var original_jquery = $().noConflict();
original_jquery.getScript("http://code.jquery.com/jquery-1.4.4.js");
var new_jquery = $().noConflict();
window.$ = original_jquery;
Then use the new_jquery as your version of jquery? I haven't tested this to see if it would work, but you might have some luck with it.
==== Final edit
As you mentioned, my javascript above wasn't exactly correct, so I tried out a few things in the console. And yes, you don't need to save the old version of jQuery because jQuery does that in the noConflict method. So just call getScript, then noConflict but saving to a new variable:
>> $.fn.jquery
"1.4.2"
>> $.getScript("http://code.jquery.com/jquery-1.4.4.js");
undefined
>> $.fn.jquery
"1.4.4"
>> var new_jquery = $.noConflict();
undefined
>> new_jquery.fn.jquery
"1.4.4"
>> $.fn.jquery
"1.4.2"
Have you tried to use JQuery.noConflict(). This sounds like it could help you.

Zend headScript() and appendFile not working as expected

I'm having an issue trying to append a javascript file using headScript()->appendFile('file name') with Zend. I have my layout setup like this:
<?= $this->headScript()
->prependScript( 'BASE_URL = "' . $this->baseUrl() . '";' )
->appendFile( $this->baseUrl('js/lib/jquery/jquery-1.4.2.min.js') )
->appendFile( $this->baseUrl('js/admin.js') );
?>
Then, in my controller I am trying to append an additional js file only for this page, like:
$this->view->headScript()->appendFile( 'another/js/file.js' );
This file needs to be appended to what is already setup in the layout. However, this file gets added before the other 'appendFile' files. I've also tried
$this->headScript()->offsetSetFile(999, '/js/myfuncs.js');
But this still adds the file before the other files. This is not how I would expect this to work, especially when using the offsetSetFile method. How can I add this file after the other files? Thanks.
The answer is to use headScript()->prependFile in layout.phtml and headScript()->appendFile in a view.
I know it's a late answer but!
If you do appendFile or appendScript is uses the next available index. Thus
$this->headScript()->offsetSetFile(50, 'file');
$this->headScript()->appendFile('file2');
is equivalent to
$this->headScript()->offsetSetFile(50, 'file');
$this->headScript()->offsetSetFile(51, 'file2');
Also it is key to note that the controller code get executed first before the view/layout code. Thus in your case your appends are actually using the id's 1000, and 1001. A quick fix to this is just to explicitly use offsetSetFile for all your appends. So your code in your layout would look like:
<?=
$this->headScript()
->prependScript( 'BASE_URL = "' . $this->baseUrl() . '";' )
->offsetSetFile(500, $this->baseUrl('js/lib/jquery/jquery-1.4.2.min.js') )
->offsetSetFile(501, $this->baseUrl('js/admin.js') );
?>
I hope this helps future googler's
I've noticed that if I 'prepend' all the files in the layout, I can then use appendFile in my controller and it will appear after them. Unfortunately, then I have to list all my JS files in reverse order in the layout (since they prepend to themselves). I'd really like to keep things in order and just be able to append to my layout stack.
If prepending in the layout file is not good enough, you can either include the files in your bootstrap when you set up your view or you could set up a controller plugin if you need to prepend based on what module is being loaded.
// in your bootstrap
protected function _initHeadScript()
{
$this->bootstrap('view');
$view = $this->getResource('view');
$view->headScript()->appendFile('another/js/file.js');
}
// as a plugin
class My_Plugin_HeadScriptPlugin extends Zend_Controller_Plugin_Abstract
{
public function routeStartup(Zend_Controller_Request_Abstract $request)
{
$view = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer')->view;
if($request->getModuleName() == 'foo')
{
$view->headScript()->appendFile('another/js/file.js');
}
}
}
Actually, you don't need get the baseUrl 'cause ZF already does it for you. You just have to pay attention to your path. Do not use the first slash! otherwise ZF will return the remote address.
Just use '$this->_view->headLink()->appendStylesheet('css/main.css');'
http://zendframework.com/issues/browse/ZF-3282?focusedCommentId=23552&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-23552
the last comment gives an excellent solution, first include in the layout at top, then print when you need it, this way it will work

Categories

Resources