Use of dojo/select on non-AMD code - javascript

I have on my site legacy JavaScript that uses Sizzle as selector engine.
I recently added the dojo library (v 1.8) for visualization purposes (charts, etc.). Because dojo includes selectors (dojo/select), I am thinking that Sizzle is now redundant and that I could replace it with dojo/select. Is there a way to make dojo/select work with non-AMD code?

Brandon Boone's answer is very useful so you do not have to rewrite your selector strings. I think what you are asking for is how to export dojo/query into global namespace, i.e. into window object through asynchronous nature of AMD. There are two options:
If you use release version, it has dojo/query already packed in dojo.js, so you do not have to take care of asynchronous execution of module factory function, just export the variable:
<script
src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.0/dojo/dojo.js"
data-dojo-config="async:true"
></script>
<script>
// export query module to global namespace (window object)
require(["dojo/query"], function(query) {
window.query = query;
});
</script>
<script>
// query function is now available globally
console.log(query("li"));
</script>
See this example in action at jsFiddle: http://jsfiddle.net/phusick/gvnGu/
If you use baseless dojo, it would be more tricky because you actually have to wait for dojo/query to load:
<script src="dtk-sdk/dojo/dojo.js" data-dojo-config="async:true"></script>
<script>
// wrap your lecacy code into a function so it's not executed immediately
var executeNonAmdCode = function() {
console.log(query("li"));
}
</script>
<script>
require(["dojo/query"], function(query) {
// export query module to global namespace (window object)
window.query = query;
// execute the legacy code
executeNonAmdCode();
});
</script>

According to the docs you can swap out the DOJO selector engine for AMD/Dojo compatible versions of sizzle or slick. So if I were you, I'd keep Sizzle around and change dojo's underlying selector to Sizzle, removing the redundancy without having to touch legacy code.
We can also use other selector engine levels. Both Sizzle and Slick
are excellent selector engines that work with dojo/query. AMD/Dojo
compatible versions (just wrapped with AMD) are available here:
https://github.com/kriszyp/sizzle
https://github.com/kriszyp/slick
Once installed, you can use the selector engine module id as specified
selector engine level. We could set Sizzle as the query engine for our
page:
<script data-dojo-config="selectorEngine: 'sizzle/sizzle'" src="dojo/dojo.js">
</script>
or set Slick as the engine for a particular module:
define(["dojo/query!slick/Source/slick"], function(query){
query(".someClass:custom-pseudo").style("color", "red");
});

To add to #phusick's answer, I just learnt that starting with v1.8 dojo supports this:
<script type="dojo/require">
"myApp.query": "dojo/query"
</script>
The above code will add the query method to the global myApp object (and create myApp if needed).
Source:
http://dojotoolkit.org/reference-guide/1.8/dojo/parser.html#dojo-parser

Related

DOJO reference error: declare is not defined

I was following the jsfiddle link http://jsfiddle.net/phusick/894af and when I put the same code into my application, I was getting "reference error: declare is not defined". I have following declarations on top of my js file:
dojo.require("dojo._base.declare");
dojo.require("dojox.form.CheckedMultiSelect");
Thanks in advance for your help.
With Dojo AMD you can tell which module maps to which parameter, for example dojo/_base/declare which is mapped to a variable called declare.
However, in non-AMD code you don't have this possibility. In stead of that you have to do the following:
dojo.require('dojo._base.declare'); // Import
dojo.declare(/** Parameters */); // Use
And actually, modules in dojo/_base are already inside the Dojo core if I'm not mistaken, so you could leave away the dojo.require() line in this case.
For the following AMD code:
require(["dojo/_base/declare"], function(declare) {
var MyCheckedMultiSelect = declare(CheckedMultiSelect, {
/** Stuff */
});
});
You can write the following in non-AMD:
var MyCheckedMultiSelect = dojo.declare(CheckedMultiSelect, {
/** Stuff */
});
However, make sure that when you're running Dojo 1.7, that you disable async mode, for example:
<script>
dojoConfig = {
parseOnLoad: false,
async: true
};
</script>
This rule applies to most, if not all, modules in dojo/_base and several DOM modules, for example:
dojo/_base/xhr: Methods like put(), get(), ... become dojo.xhrGet(), dojo.xhrPut(), ...
dojo/_base/lang: Methods like mixin(), hitch(), ... become dojo.mixin(), dojo.hitch(), ...
dojo/dom: Methods like byId() become dojo.byId()
dojo/on: You have to use dojo.connect() for this
dijit/registry: Methods like byId() become dijit.byId()
...
However, if you're using Dojo 1.7, then you should probably just leave the code in AMD even if all other code is written in non-AMD code. Eventually you will have to upgrade all your code to AMD-syntax, if you're now investing time to convert the code to non-AMD and you later have to convert it to AMD again, you're doing the same work twice.

Trying to use Dijit but getting: Dojo is not defined

I am trying to include a demo dijit widget in my template. However, I keep getting: "Dojo is not defined". I tried to find an answer, but I couldn't. This error is odd as the required dojo.js is indeed loaded by the browser.
What is not working is:
dojo.require("dijit.form.MultiSelect");
However, if I use other dojo modules doing:
require(["dojo/store/JsonRest" ...
It works.
I made pastebin of my template here: http://pastebin.com/9fm13pSP
Is this Dojo 1.7?
For that version, I think Dojo expects the functions define() and require() to be defined in the global namespace.
These functions are then used to require dojo itself. Your dojo.js may have all the libraries in it, but they might not be available to use until you explicitly require them.
This worked for me (using Dojo 1.7.2):
<script>
// Dojo 1.7 (AMD)
require(["dojo"], function(dojo){
dojo.require("dijit.form.MultiSelect");
dojo.ready(function(){
var sel = dojo.byId('dynamic');
var n = 0;
for(var i in dijit){
var c = dojo.doc.createElement('option');
c.innerHTML = i;
c.value = n++;
sel.appendChild(c);
}
new dijit.form.MultiSelect({ name: 'dynamic' }, sel);
});
});
</script>
As Paul Grime pointed out, if you include Dojo 1.7 with the async:true flag set (as you did) it will only expose the require and define functions to the global namespace. This is part of the transition to the new style of AMD modules that was recently introduced.
You can now choose to either
Add that little "require dojo" wrapper around the code
Remove the "async:true" flag and rely on back-compatibility with 1.6
Convert your code to using the new 1.7 AMD asynchronous modules instead of the old synchronous style with "dojo.require".

RequireJS Flawed By Example?

Looking into RequireJS but unlike Head.JS which downloads in undetermined order but evaluates in a determine order, RequireJS seems different
Normally RequireJS loads and evaluates scripts in an undetermined order.
Then it shows how to prefix order! to the script names for explicit ordering etc..
Then in the examples:
require(["jquery", "jquery.alpha", "jquery.beta"], function($) {
//the jquery.alpha.js and jquery.beta.js plugins have been loaded.
$(function() {
$('body').alpha().beta();
});
});
So if jquery.alpha is downloaded and evaluated before jquery then surely this would cause a problem? Forgetting any client code usage such as function body above, if like most plugin they attach to jQuery.fn then at stage of evaluation then jQuery will undefined in this scenario.
What am I missing here?
RequireJS is not designed to load plain javascript, but to load defined modules. The module format looks something like:
define(['a', 'b'], function(a, b) {
return { zzz: 123 };
});
The important thing to note is that all of the module code is inside an anonymous function. So if the file is run in an arbitrary order, it doesn't matter, because all it does is register the module. The module code is then run in dependency order, with the return value becoming the module object, which is passed as a parameter to code that uses the module.
If you are trying to load plain files, this will not work correctly for you. There is the order plugin to force load order in that case.
It should be noted that that example uses the custom made version of "requirejs and jquery" packaged together, which I believe means that jquery will always be available first.
If you have problems, you can always wrap your plugins within a module definition and make sure they depend on jquery themselves, again ensuring the order is correct:
/* SPECIAL WRAPPING CODE START */
define(['jquery'], function(jQuery) {
// .... plugin code ....
/* SPECIAL WRAPPING CODE END */
});
You are correct, without something to aid in the order an exception will occur. The good news is RequireJS has an Order plug-in to help in this.
I'm currently evaluating RequireJS...
And Here Is An Example of One of My Files:
The 'order!' command will load files for you sequentially. You can (then) use the callback to load other (support) files.
<script src="Loaders/RequireJS/requireJS.js" type="text/javascript"></script>
<script src="Loaders/RequireJS/order.js" type="text/javascript"></script>
<script type="text/javascript">
require(["Loaders/RequireJS/order!././Includes/JavaScript/jQuery/Core/jquery-1.3.2.js",
"Loaders/RequireJS/order!././Includes/JavaScript/jQuery/Core/jquery.tools.min.js",
"Loaders/RequireJS/order!././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.tmpl.js"], function() {
require(["././Includes/JavaScript/jQuery/jGeneral.js",
"././Includes/JavaScript/jQuery/autocomplete.js",
"././Includes/JavaScript/jQuery/jquery.ErrorWindow.js",
"././Includes/JavaScript/jQuery/jquery.ValidationBubble.js",
"././Includes/JavaScript/jQuery/jquery.Tootltip.js",
"././Includes/JavaScript/jQuery/jquery.Extensions.js",
"././Includes/JavaScript/jQuery/jquery.Toaster.js"], null);
require(["././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.dimensions.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jQuery.Color.Animations.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.corners.min.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.tipsy.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.numberformatter-1.1.0.js",
"././Includes/JavaScript/jQuery/ThirdPartyPlugIns/jquery.tipsy.js"], null);
});
</script>
In All Honesty:
I'm looking at various asynchronous resource-loaders and I'm having a hard-time finding one that does everything I need. I'm also finding the documentation in each one lacking.

How to isolate different javascript libraries on the same page?

Suppose we need to embed a widget in third party page. This widget might use jquery for instance so widget carries a jquery library with itself.
Suppose third party page also uses jquery but a different version.
How to prevent clash between them when embedding widgets? jquery.noConflict is not an option because it's required to call this method for the first jquery library which is loaded in the page and this means that third party website should call it. The idea is that third party site should not amend or do anything aside putting tag with a src to the widget in order to use it.
Also this is not the problem with jquery in particular - google closure library (even compiled) might be taken as an example.
What solutions are exist to isolate different javascript libraries aside from obvious iframe?
Maybe loading javascript as string and then eval (by using Function('code to eval'), not the eval('code to eval')) it in anonymous function might do the trick?
Actually, I think jQuery.noConflict is precisely what you want to use. If I understand its implementation correctly, your code should look like this:
(function () {
var my$;
// your copy of the minified jQuery source
my$ = jQuery.noConflict(true);
// your widget code, which should use my$ instead of $
}());
The call to noConflict will restore the global jQuery and $ objects to their former values.
Function(...) makes an eval inside your function, it isn't any better.
Why not use the iframe they provide a default sandboxing for third party content.
And for friendly ones you can share text data, between them and your page, using parent.postMessage for modern browser or the window.name hack for the olders.
I built a library to solve this very problem. I am not sure if it will help you of course, because the code still has to be aware of the problem and use the library in the first place, so it will help only if you are able to change your code to use the library.
The library in question is called Packages JS and can be downloaded and used for free as it is Open Source under a Creative Commons license.
It basically works by packaging code inside functions. From those functions you export those objects you want to expose to other packages. In the consumer packages you import these objects into your local namespace. It doesn't matter if someone else or indeed even you yourself use the same name multiple times because you can resolve the ambiguity.
Here is an example:
(file example/greeting.js)
Package("example.greeting", function() {
// Create a function hello...
function hello() {
return "Hello world!";
};
// ...then export it for use by other packages
Export(hello);
// You need to supply a name for anonymous functions...
Export("goodbye", function() {
return "Goodbye cruel world!";
});
});
(file example/ambiguity.js)
Package("example.ambiguity", function() {
// functions hello and goodbye are also in example.greeting, making it ambiguous which
// one is intended when using the unqualified name.
function hello() {
return "Hello ambiguity!";
};
function goodbye() {
return "Goodbye ambiguity!";
};
// export for use by other packages
Export(hello);
Export(goodbye);
});
(file example/ambiguitytest.js)
Package("example.ambiguitytest", ["example.ambiguity", "example.greeting"], function(hello, log) {
// Which hello did we get? The one from example.ambiguity or from example.greeting?
log().info(hello());
// We will get the first one found, so the one from example.ambiguity in this case.
// Use fully qualified names to resolve any ambiguities.
var goodbye1 = Import("example.greeting.goodbye");
var goodbye2 = Import("example.ambiguity.goodbye");
log().info(goodbye1());
log().info(goodbye2());
});
example/ambiguitytest.js uses two libraries that both export a function goodbye, but it can explicitly import the correct ones and assign them to local aliases to disambiguate between them.
To use jQuery in this way would mean 'packaging' jQuery by wrapping it's code in a call to Package and Exporting the objects that it now exposes to the global scope. It means changing the library a bit which may not be what you want but alas there is no way around that that I can see without resorting to iframes.
I am planning on including 'packaged' versions of popular libraries along in the download and jQuery is definitely on the list, but at the moment I only have a packaged version of Sizzle, jQuery's selector engine.
Instead of looking for methods like no conflict, you can very well call full URL of the Google API on jQuery so that it can work in the application.
<script src="myjquery.min.js"></script>
<script>window.myjQuery = window.jQuery.noConflict();</script>
...
<script src='...'></script> //another widget using an old versioned jquery
<script>
(function($){
//...
//now you can access your own jquery here, without conflict
})(window.myjQuery);
delete window.myjQuery;
</script>
Most important points:
call jQuery.noConflict() method IMMEDIATELY AFTER your own jquery and related plugins tags
store the result jquery to a global variable, with a name that has little chance to conflict or confuse
load your widget using the old versioned jquery;
followed up is your logic codes. using a closure to obtain a private $ for convience. The private $ will not conflict with other jquerys.
You'd better not forget to delete the global temp var.

mootools: $ not defined

I've strange error i don't understand. I'm just moving my code from jQuery over to Mootools because it's much cleaner than the jQuery mess.
Now when i use the
$$('div.canvas')
in mootools i get the correct element.
When i try
$(document).getElement('div.canvas')
it tells me that $ is not defined. How can $$ and all helper functions like $lambda etc. be defined but not $?
Has something changed there from 1.1 to 1.2 and the docs are not updated yet?
as someone pointed out, when $ is defined, mootools 1.2.3+ will not take it over, it will revert to using document.id instead. this did not used to happen before that release so it largely depends on the version you are referencing. but it's certainly changed since 1.11 and it IS documented, read the announcement here http://mootools.net/blog/2009/06/22/the-dollar-safe-mode/
to your application design this means that, if your structure is...
load jquery (no need for noconflict, does not matter)
load mootools
... it can work as follows:
$("#foo"); // jquery
document.id("foo"); // mootools
// or create a temporary scope for mootools things
(function($) {
$("foo"); // mootools
})(document.id);
best / recent practices in mootools development require plugins and code released to reference document.id or be within such a closure to ensure compatibility. this is actually ok as unlike in jquery where $ aliases the jQuery singleton, in mootools $ is just a selector so its use will be far less spread. Hence typing document.id("selector") is not going to be that much of a drag.
Have you removed all reference to jQuery in your htmls?
MooTools will not override the $ function if it exists already. It checks for nullness of $ before defining it. So I suspect the $ is still lurking somewhere.
if (window.$ == null) Window.implement({
$: function(el, nc){
return document.id(el, nc, this.document);
}
});
After including jQuery and running $.noConflict(); but before including MooTools, can you log the contents of $ and see what is logged?
include jquery
$.noConflict();
console.log($); // should return undefined
include mootools
If you are using both libraries on the same page you must use JQuery's noConflict() function
<script type="text/javascript" src="other_lib.js"></script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$.noConflict();
jQuery(document).ready(function($) {
// Code that uses jQuery's $ can follow here.
});
// Code that uses other library's $ can follow here.
</script>
If you are still having trouble, try checking through your included JQuery files to ensure that any plugins/code use jQuery('div.canvas') etc instead of $ as $ has been released by the noConflict() function and will not run JQuery code.

Categories

Resources