React - Using external js from CDN - javascript

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'

Related

Using require.js to load something before onload

I see this question was asked here
load javascript file before onload with requirejs
But the solution there doesn't fit my situation and so I'm wondering if there is a different solution. I can't build with deps and make my code come last because I'm making a library/utility, not an app.
I'm working on the WebGL-Inspector. It works by wrapping HTMLCanvasElement.prototype.getContext and if it sees a "webgl" it then does its thing wrapping the context and allowing you to inspect it.
The WebGL-Inspector works in 3 modes
As a browser extension
As a loader + large compiled script
As a loader + original source (many many scripts)
To use it in modes #2 or #3 above you just insert
<script src="core/embed.js"></script>
Somewhere in the top of your HTML. Because it loads synchronously~ish it will have wrapped HTMLCanvasElement.prototype.getContext before whatever scripts come after it.
These last 2 modes are mostly for debugging/development of the WebGL-Inspector itself. Especially mode #3 because we can edit the inspector's code and refresh the page immediately to see the result, no build step.
I'm in the process of switching to using AMD for all of the WebGL-Inspector. We're using this because we can use webpack to make #2 but still follow the same dev workflow allowing us it use mode #3 above just change the script tag to
<script src="core/require.js" data-main="core/embed.js"></script>
The problem is this no longer works because whatever other code unrelated to the WebGL-Inspector itself runs before core/embed.js has loaded and so calls someCanvas.getContext before we've had a chance to wrap it.
My current solution is sadly to hack in a delay of 1.5 seconds on whatever demo we're using
<script src="core/require.js" data-main="core/embed.js"></script>
<script>
// wait 1.5 seconds for embed.js to load and pray :(
window.onload = setTimeout(reallyRunWebGLCode, 1500);
...
</script>
The previous non-AMD loader doesn't have this async issue. Somehow it manages to load 60 or so .js files before window.onload fires. Is there a way to get require.js to do the same? Or maybe I need to write my own loader?
To put it another way, the issue is as it is now the user adds a single <script> line and makes no other changes to their code and it works. When they're done they remove the single script line.
Switching to AMD/require.js the user is now required to add a single script and re-write code. In my tests went from
window.onload = myapp;
to
require.config({ baseUrl: "/core" });
require("./embed", myapp);
It's minor but now the app is broken when you remove the <script> tag and has to be put back as it was. Requiring those changes is what I'm trying to avoid if possible. In fact with the original style you don't even have to remove the script tag, just run your app in an environment where the script doesn't exist, it will fail to load and your app will run as normal where as with the require method it will fail if the script doesn't exist.
I can require even more code
if (typeof require === 'function' && typeof define === 'function' and define.amd) {
require.config({ baseUrl: "/core" });
require("./embed", myapp);
} else {
window.onload = myapp;
}
But that's even uglier. We went from adding a single script to requiring modifying your app.
Actually it gets even worse in the app I'm currently testing with. It uses a different loader for itself. When I run require version of the code above it fails because in this case require runs myapp before window.onload (strange). I end up having to do this
var tryRunCount = 0;
function tryRunApp() {
++tryRunCount;
// check that tryRunApp has been called twice so we know
// both onload and require have returned
if (tryRunCount === 2) {
myApp();
}
}
window.onload = tryRunApp;
require.config({ baseUrl: "/core" });
require("./embed", tryRunApp);
That's way way more changes than I want to require users to make.

how should I write my define to work with curl.js?

I'm reading Addy Osmani's excellent blog post about writing AMD modules. I start with a simple chunk of js that I lifted from his post:
define('modTest', [],
// module definition function
function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
I took out the dependencies on foo and bar. Just want a simple object that logs to the console.
So I save that in /js/modTest.js and then try to load it:
curl(['/js/modTest.js'])
.then(function(mt) {
console.log("Load complete");
console.log("mt:");
console.log(mt);
mt.doStuff()
}, function(ex) {alert(ex.message);})
Result: error: Multiple anonymous defines in URL. OK that didn't work. Tried adding in a namespace: define('myCompany/modTest', [],, same result. Tried adding an empty string in the dependency array, same result.
Also tried curl(['modTest.js'], function(dep){console.log(dep)}); with the same result.
Is the code in Addy's blog post incorrect? Am I doing something wrong? Maybe a bug in curl?
Update 5/24: I ditched curl.js in favor of require.js. Zero odd errors, very little work to change over. I did have to deal with amdefine a bit to get my code running client and server side (one object is in both places, so grunt had to be configured to take care of that). My defines generally look like:
define(->
class AlphaBravo
...
And never have any trouble loading.
You asked curl() to fetch a module called "/js/modTest.js". It found the file and loaded it and found a module named "modTest", so it complained. :) (That error message is horribly wrong, though!)
Here's how you can fix it (pick one):
1) Remove the ID from your define(). The ID is not recommended. It's typically only used by AMD build tools and when declaring modules inside test harnesses.
2) Refer to the module by the ID you gave it in the define(). (Again, the ID is not recommended in most cases.)
curl(['modTest'], doSomething);
3) Map a package (or a path) to the folder with your application's modules. It's not clear to me what that would be from your example since modTest appears to be a stand-alone module. However, if you were to decide to organize your app's files under an "app" package, you packages config might look like this:
packages: [ { name: 'app', location: 'app' } ]
Then, when you have code that relies on the modTest module, you can get to it via an ID of "app/modTest".
curl(['app/modTest'], doSomething);
I hope that helps clear things up!
Fwiw, Addy's example could actually work with the right configuration, but I don't see any configuration in that post (or my eyes missed it). Something like this might work:
packages: [ { name: 'app', location: '.' } ]
-- John
I've just had a similar problem which turned out to be the include order I was using for my other libraries. I'm loading handlebars.js, crossroads.js, jquery and a few other libraries into my project in the traditional way (script tags in head) and found that when I place the curl.js include first, I get this error, but when I include it last, I do not get this error.
My head tag now looks like this:
<script type="text/javascript" src="/js/lib/jquery.js"></script>
<script type="text/javascript" src="/js/lib/signals.js"></script>
<script type="text/javascript" src="/js/lib/crossroads.js"></script>
<script type="text/javascript" src="/js/lib/handlebars.js"></script>
<script type="text/javascript" src="/js/lib/curl.js"></script>
<script type="text/javascript" src="/js/main.js"></script>
You have a problem with your define call. It is NAMED
See AMD spec for full story on how to write defines, but here is what I would expect to see in your js/modTest.js file:
define(/* this is where the difference is */ function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
Now, the boring explanation:
CurlJS is awesome. In fact, after dealing with both, RequireJS and CurlJS, I would say CurlJS is awesome-er than RequireJS in one category - reliability of script execution ordering. So you are on the right track.
On of the major things that are different about CurlJS is that it uses "find at least one anonymous define per loaded module, else it's error" logic. RequireJS uses a timeout, where it effectively ignores cases where nothing was defined in a given file, but blows up on caught loading / parsing errors.
That difference is what is getting you here. CurlJS expects at least one anonymous (as in NOT-named) define per loaded module. It still handles named defines fine, as expected. The second you move the contents of "js/modTest.js" into inline code, you will have to "name" the define. But, that's another story.

Why isn't RequireJS passing jQuery into the functions alias?

I downloaded the sample-project for the latest-release of RequireJS. Their documentation implies anything loaded is passed-into the parameter list of the associated function (in corresponding order).
So I decided to try it...but it doesn't seem to work!
Firebug (net tab) shows jQuery as being loaded: so RequireJS obvioulsy did that part successfully
Firebug (console tab) shows '$ is not a function'
My question is: Why isn't the alias getting populated?
MY CODE LOOKS LIKE:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="Scripts/require.js" type="text/javascript"></script>
<script type="text/javascript">
require(["scripts/jQuery/Core/jquery-1.7.2.min"], function ($) {
// jQuery is not passed-into the function, so the alias fails!
$(function () {
var stop = "";
});
});
</script>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
THIER SAMPLE LOOKS LIKE:
//Inside scripts/main.js
require(["some/module", "a.js", "b.js"], function(someModule) {
//...
});
jQuery should be loaded through the special name "jquery", otherwise it won't register itself (since jQuery uses a named define).
// create an alias that points to proper file
require.config({
paths : {
jquery : "http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min"
}
});
// require jquery usign the path config
require(["jquery"], function ($) {
console.log($);
});
That is the main reasons why named define is considered an anti-pattern and should be used only when needed (when you have multiple modules inside same file).
Be sure to read the "README.md" for the RequireJS+jQuery sample project. There are many complications with using jQuery that you need to decide on how best to tackle it for your project during the initial setup. Once you've figured out what's best for you and implemented it, though, it shouldn't be a problem again.
Much of the complication also comes from the fact that jQuery isn't a true AMD module, they just have a hack in the codebase to do a define if it detects the define function is available. For example, this means the jQuery module name will always be "jquery" (note the lowercase 'q') unless you wrap it yourself, so if you define the path for it in your config you MUST have the key named "jquery" (again, with a lowercase 'q') or there will be a mismatch. That bit us when first setting up RequireJS on our project (we named the key "jQuery").
Is your scripts folder 'Scripts' or 'scripts'?
You're making a request for "Scripts/require.js" as well as for "scripts/jQuery/Core/jquery-1.7.2.min". Take a look at the Net tab of Firebug... I bet you're generating a 404 there.

Function Undefined: Must a javascript function be defined within the same file?

I've got a file notifications.js containing one event bound to an element, and a function updateNotification(). This function uses jQuery to update certain elements on the page when a JSON object is passed as a parameter.
The problem:
I'm attempting to call this function within the page (via <script> tags), however rather than calling it, it breaks the page. I did some digging around within the Chrome Developer Console (not sure of the name), and an error is flagged:
/pleaseshare/views/install/:50 Uncaught ReferenceError:updateNotification is not defined
However, when I pan within the console, I can clearly see the file notifications.js listed under scripts, and the function is defined there. If I define the function within the current scope (e.g. the line above the call), it works fine.
What I've tried
The function contains some javascript that requires jQuery, so I've attempted both with and without encasing it in $(document).ready( function() {});, with neither seeming to have any affect.
I'm pretty stumped.
For good measure, here's a link to show the structure of my javascript and html: http://snippi.com/s/znk6xe9
Any help in figuring out why this is happening, or explanations of why javascript functions cannot be called cross-file (although I'd hope this isn't the case), would be greatly appreciated ;)!!
A function cannot be called unless it was defined in the same file or one loaded before the attempt to call it.
A function cannot be called unless it is in the same or greater scope then the one trying to call it.
You code looks like the structure should work, but is clearly a reduced test case that has been reduced to the point where it won't.
Got it working. The issue was definitely multi-faceted, but I figured it out.
First off the use of RequireJS had an impact on updateNotification(), in that it couldn't be called cross-file, and was therefore considered undefined. I assume this because of how RequireJS loads files, and I'll look into the documentation later (and post an edit if I find anything relevant).
Secondly, updateNotification() would again be considered undefined when encased within jQuery's DOM ready loader $(document).ready(function(){}). However updateNotification() contains executions which require jQuery, so I had to encase the contents of the function in $(document).ready(function(){}).
This is an issue very unique to RequireJS/jQuery, hence why in most use cases this wouldn't occur.
Side note: The tags are edited to reflect this.
you need to import your script into your page:
<script type="text/javascript" language="javascript" src="path/to/notifications.js"></script>
This needs to be added above the <script> tag that calls updateNotification()
Functions do not need to be declared in the same file. In fact, avoiding having every declaration dumped into the global namespace is usually a concern in JavaScript.
In the sample code in the link you provided, updateNotification is declared as a global, so there should not be a scoping problem.
However, in the same sample, you don't show notifications.js being included. You need to import it using a <script></script> element and that element must come before the script element that includes the call to updateNotification. You also must include jQuery before notifications.js, since it uses jQuery. So you need something like:
<body>
// One or two elements
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="notifications.js"></script>
<script type="text/javascript">
$(document).ready( function() {
var json = {status : 'ok', message: 'Hello'};
updateNotification(json);
});
</script>
// All other elements
</body>

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.

Categories

Resources