Electron: jQuery isn't set globally because "module" is defined

Created on 6 May 2014  ·  71Comments  ·  Source: electron/electron

jQuery contains something along this lines:

if ( typeof module === "object" && typeof module.exports === "object" ) {
  // set jQuery in `module`
} else {
  // set jQuery in `window`
}

module is defined, even in the browser-side scripts. This causes jQuery to ignore the window object and use module, so the other scripts won't find $ nor jQuery in global scope..

I am not sure if this is a jQuery or atom-shell bug, but I wanted to put this on the web, so others won't search as long as I did.

Most helpful comment

Just unset module.

I wanted to have my webpage work both in the browser and in electron.
As explained in this thread, electron exposes a global module when nodeIntegration: true, which jQuery then sees and reacts accordingly:

if ( typeof module === "object" && typeof module.exports === "object" ) {
    // bind to module
} else {
    // bind to window
}

I was able to get jQuery to bind to the window object and use nodeIntegration: true by doing the following in my html file:

<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script src="//code.jquery.com/jquery-1.12.0.min.js"></script>
<script src="//code.highcharts.com/highcharts.js"></script>
<script>if (window.module) module = window.module;</script>

Hope this helps someone.

All 71 comments

This isn't really a bug for either system, you have the same problem when using browserify. jQuery sees that its running in a CommonJS environment and expects to be used as such.

The solution is relatively simple, however. Instead of loading jQuery as a script tag, load it via require:

window.$ = window.jQuery = require('/path/to/jquery');

I agree with @ChiperSoft, this is expected behavior in CommonJS environment, so I marked this as wontfix. And thanks for sharing your finding.

@ChiperSoft, Where should I put the jquery.js file to get require('jquery') working? I've tried to put it at the same level as package.js and main.js but the runtime doesn't seem to pick it up.

You can use require('jquery.js') note the .js at the end, to load jquery relative to your HTML file.

Sorry, I used window.$ = window.jQuery = require('/path/to/jquery'); as mentioned but it doesn't work,
Uncaught Error: Cannot find module 'scripts/jquery-1.10.2.min.js' module.js:339

I use that all the time and it works great! Here is what I use, be mindful about the path:

window.$ = window.jQuery = require('./scripts/jquery-2.1.1.min.js');

@frankhale oh, it worked with ./, thanks!

Awesome! The path thing tripped me up as well.

If your app does not need node-integration, add "node-integration": false to your BrowserWindow options. In this case module.exports is not available, jQuery works like expected.

@kabalage worked wonderfully, thanks!

:+1:

Walked up to the same issue. Install jQuery via NPM then required it and it worked.

what if node integeration and jquery without the require is needed...

I'm trying to make sense of this since it appears to be something I could use. However I wonder when can you be sure you can safely use $. Logging $ right after this line will result would be require and not jquery since require is asynchronus?

window.$ = window.jQuery = require('/path/to/jquery');
console.log($) // logs 'require' not 'jQuery'

@dieroux try installing it via npm:

npm install jquery

This worked for me while trying the same. Then you don't need to give a path, you just say:

window.$ = window.jQuery = require('jquery');
console.log( [$, jQuery ]);

What if we need node-integration, and we can't use require('something') ?
Currently it works by doing "delete module;" before loading problematic-stuff.
What's the impact of doing this ?

I'm having similar issues where jquery can be loaded correctly the first time I need it in a preload script on a webview. However the second time jquery is referenced from an external page, the line to assign it via:

  try {
        module.exports = null;
        if (typeof require === 'function') {
          alert('window: ' + window);
          alert('jquery: ' + window.jQuery);
          alert('export: ' + module.exports);
          if ( !window.jQuery ) {
            alert('before load');
            window.jQuery = require('./node_modules/jquery/dist/jquery.js');
            alert('after load');
            window.$ = window.jQuery;
            alert('after assignment');
         }
          alert('jquery: ' + window.jQuery);
        }
    } catch(e) {
        alert('failed load.');
        console.log(e);
    }

throws a Cannot read property createElement of undefined in the console at the assignment to window.jQuery from require, resulting in jQuery not getting loaded.

Error message on the console is:
Guest page logged a message: TypeError: Cannot read property 'createElement' of undefined line: 40

Any additional thoughts on how to successfully load jquery on subsequent pages?

Thanks.

This was the first thing I tripped over when running through the electron quickstart and tried to wrap a local app - something working in chrome suddenly doesn't in election. Having the error buried in failed angular module loads doesn't help of course, so I assumed there was an issue with script load order, which is how I found this thread. Perhaps the keywords will help others.

@kabalage 's solution above worked perfectly.

Hello all ,I just want add some info with this issue , If i paste the jquery library code in electron console $ and jQuery is added as a function in window object. i could able to run jquery.

Referenced jQuery with script tag, same error. Have to "node-integration": false in BrowserWindow()

Hi guys... I'm not sure why this issue is closed, there is definitely still an issue here, let me explain. If I install jQuery locally via NPM and load a local html file then using:

mainWindow.$ = require('jQuery');
mainWindow.jQuery = require('jQuery');

then it works as expected. If, however, I do mainWindow.loadUrl('http://external.site.com");

I get the errors mentioned where $ and jQuery are not defined. Disabling node integration is really not an acceptable answer; it's the reason I'm using Electron in the first place, I need it.

I'd be keen to get into the source and troubleshoot this myself but thought I would see if anyone had any additional information on this to point me in the right direction before I go down there. Has anyone been able to get this to work _with_ node integration, against external sites?

Thanks... and great work BTW. Everything new and beautiful, like this, is going to start with a few wrinkles.

Kind Regards

/W

I made it work with node-integration.

  1. Install it with npm, so it is defined in package.json
  2. Use require with module name <script>window.$ = window.jQuery = require('jquery');</script>
  3. Don't use path in require parameter.
  4. Don't install trough bower, it will make you to use path in require parameter.
  5. If you use bootstrap you need to load jQuery first.

All of above supported only by my experience.

Will work

    <script>window.$ = window.jQuery = require('jquery');</script>
    <script src="path_to/bootstrap.js"></script>

Will not

    <script>window.$ = window.jQuery = require('./path/to/jquery.js');</script>
    // or
    <script src="path_to/jquery.js"></script>
    <script src="path_to/bootstrap.js"></script>

Hi dilmdeep, thanks for the quick response. Does your solution depend on having control over the external site? I'm trying to load a site that I don't have source access to. What you said about packages.json is interesting though. I did install jQuery through npm (and used the documented rebuild for electron) and, like I said, I can use jQuery with a local HTML file, but jQuery is not defined in my package.json..... Oh and I tested with disabling node-integration and while that does work, it's not what I need. Thanks.

I just wanted to update this in case someone else gets into the same trouble. The way I worked around this (after downloading 10GB of Electron source) was actually in the documentation to begin with. Instead of calling the url of the external site I loaded a local HTML file and used a webview from that html file to load the external site. It loads perfectly with node-integration enabled. Just look in the docs for webview, it's pretty straight forward. Also my thanks to Paul in the chatroom for helping me get started with the source. If anyone's wondering they use Atom (of course) to code Electron, WinDbg on windows to debug and Ninja to compile. FYI compiling the sources was dead easy and surprisingly fast, it compiled first go using their scripts and the provided documentation. Again, great work guys!

@WolfieWerewolf Bravo! have to say the example is bit misleading if security is considered.

@Shufeng01 Cheers mate, I'm coming at this cold from .Net land so feeling like a complete n00b. You're right that security was an issue however using the disablewebsecurity flag on the webview itself resolved most of it. I think security, in general, is the biggest challenge for bringing the web to the desktop... but well worth the effort. Electron rocks.

@diimdeep thanks, you solution works for me (not what @WolfieWerewolf wants!)

This is a bit of a problem for us. We bundle jquery with our app js, and this concatenated JS is then used in build for both the web and desktop versions (we have been using NWJS). NPM install may be an option for us, but it's not ideal in our situation for a couple of reasons.

Is there no theoretical way of getting it to work using script tags?

@bgSosh, why not disable node integration (read about the node-integration key here)? If you do that, script tags should work correctly. If you need some node related stuff you could do that in a preload script (they always have node integration).

@etiktin can you please elaborate how to do this in preload ? i am new to electron and havnt managed to get a preload script to execute (at least it appears not to execute, console.log not working, changing window.myExposedIPC = require('ipc') for example is not available later etc.

i have got it working in case anybody has the same issue: https://github.com/atom/electron/issues/1753#issuecomment-157409572

@matthiasg glad you got it working. I added some comments to your example.

When node integration is disabled and you want to do some node stuff in your preload script, it's important to hold a reference to what ever node global you need. You need to do this, because on the next tick the global references are deleted (this is how disabling node integration is implemented), so if you didn't hold a reference and you then try to require something on a function call or a timer, it would fail.

@etiktin thanks. Of course that was part of it, but what i was missing is the documentation for process.once('loaded', ... ) once that was in place i can actually expose the apis i wanted.

Hi guys,

Since none of the above worked for me (using w2ui and JQuery both with electron and independent HTML page, let also w2ui use jQuery), I felt the need to post my working solution here:

<html>
<head>
    <title>MyApp</title>
    <link rel="stylesheet" type="text/css" href="./lib/w2ui-1.4.3.css"/>
    <script type="text/javascript" src="./lib/require.js"></script>
    <script type="text/javascript" src="./lib/jquery-2.1.4.js" onload="$ = jQuery = module.exports;"></script>
    <script type="text/javascript" src="./lib/w2ui-1.4.3.js"></script>
</head>
<body>
<script type="text/javascript">
    // use global $ and w2ui here... 
    ...

I'm a newbie to JS and performing a technology study and now a bit worried about this obscure module loading stuff. It may be a silly question but why does electron load JS modules so differently from usual browsers when loading an HTML page?

@forman did you disable node integration and use a preload script for node interaction? See the comments above by me and @matthiasg. If you do that, jquery and the rest will work exactly like they do in the browser.

Will work

requirejs(['require', '../require.config'], function (requirejs, config) {
        //fixed jQuery isn't set globally
        requirejs(['jquery'],function(jquery){
            window.$ = window.jQuery =jquery;
        });
        // update global require config
        window.requirejs.config(config);
        // load app
        requirejs(['main'],function(main){
        });
    });

@forman Thank you !! None of the above worked excepted your solution. (using electron-prebuilt 0.35.5)
@etiktin This worked for me without having to change anything on the mainProcess side

Thought I should add our problem and solution to this thread for future reference.

We're loading an external url in a webview and need to execute our own Javascript that can use electron/Node. Enabling nodeintegration on the webview isn't an option since this will break jQuery that is being used by the external url.

What we ended up doing was adding a preload script to the webview where we put the modules we need in the global scope. This makes them accessible later on when we execute our Javascript.

preload.js:

global.ipcRenderer = require('electron').ipcRenderer;

And then we can access the ipcRenderer like:

webview.executeJavaScript("ipcRenderer.sendToHost('hello');");

Uncaught Error: Cannot find module 'jquery' and nothing helps from this thread =/

Just unset module.

I wanted to have my webpage work both in the browser and in electron.
As explained in this thread, electron exposes a global module when nodeIntegration: true, which jQuery then sees and reacts accordingly:

if ( typeof module === "object" && typeof module.exports === "object" ) {
    // bind to module
} else {
    // bind to window
}

I was able to get jQuery to bind to the window object and use nodeIntegration: true by doing the following in my html file:

<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<script src="//code.jquery.com/jquery-1.12.0.min.js"></script>
<script src="//code.highcharts.com/highcharts.js"></script>
<script>if (window.module) module = window.module;</script>

Hope this helps someone.

@tomkel here is another solution:

<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
<script>window.jQuery = window.$ = module.exports;</script>

In my case, I need to be able to both launch my application as a website and a Electron application. Here is my solution:

<!-- Launched with Electron -->
<script>
    if ('require' in window) {
        window.$ = window.jQuery = require('jquery');
    }
</script>

<!-- From CDN, Bower, etc... -->
<script src="https://code.jquery.com/jquery-2.2.0.js"></script>

<!-- Others libraries depending on jQuery -->
<script src="..."></script>

@dkfiresky Yes, that seems to work as well. However I'm using Highcharts in conjunction with jQuery. Highcharts does a similar thing, placing itself on module if it exists. For whatever reason I was unable to get

<script>window.Highcharts = module.exports;</script>

to work. I've updated my previous comment to reflect that.

@tomkel your solution with the temporal hiding of module works very well for me. My problem was during migration from nw.js with ngDialog assuming that it is in CommonJS environment failing to require('angular') and to register itself as angular module.

In Electron 0.36.7 I find that I cannot do a simple window.$ = require('jquery'); as it'd fail to find the module. I have to do window.$ = require('electron').remote.require('jquery'); to do it in the main process. The renderer doesn't seem to have the same module resolution path as the main process

Actually that doesn't work because jQuery would be running in the main process which doesn't have document. And require('jquery') didn't work for me because I'm serving the page through HTTP like #4243. To fix this I've tried adding module paths from the main process to the renderer process by:

require('electron').remote.process.mainModule.paths.forEach(function(path) {
    require('module').globalPaths.push(path);
});

After that I can do require('jquery') with no problems. Not sure what other impact this may have though

And require('jquery') didn't work for me because I'm serving the page through HTTP like #4243.

If you are allowing remote content to access node.js, your app is very very insecure, you need to disable node integration when running remote content and use a preload script

Even if the "remote content" is localhost? I'm using node to build and serve me the page instead of reading from a local file, that is all

Expanding on @dkfiresky, you might get an Uncaught ReferenceError: module is not defined in the browser version.

If so, try this:

<script src="./bower_components/jquery/dist/jquery.min.js"></script>
<script>window.jQuery = window.$ = typeof module === 'object' ? module.exports : jQuery</script> 

@dspint module will be undefined only in case of node integration is disabled, so you probably should use my example only with node integration enabled.

If you are using window.open, you can pass nodeIntegration=0 with your window options.

window.open('https://your.cool/url', 'title', 'nodeIntegration=0');

I'm doing:

<script>
  var saveModule = module;
  delete module;
</script>
  <script src="jquery.js"></script>
  <script src="jquery-ui.js"></script>
<script>
  module = saveModule;
</script>

I had the same problem as @gnail. I had a node server running a angular project. His code fixed it for me.

require('electron').remote.process.mainModule.paths.forEach(function(path) {
  require('module').globalPaths.push(path);
});

It's working for me 😄

<script>
    window['old_module'] = window['module'];
    delete window.module;
</script>

<script src="any_lib_like_jquery.js" />

<script>
    window['module'] = window['old_module'];
    delete window.old_module;
</script>

I'm having this issue, i've already tried all of this thread. but nothing works, im stuck on the beggining
of all.
i'm using the electron-starter rep

@PauloGaldo basically any solution in this thread should be working. Please post the relevant excerpt of your code and all error messages you encounter.

@ChiperSoft Can you please tell me where should I use your code?

@tomkel Very nice!
I finally got Masonry to work within Electron!

For some reason I'm still not able to make jQuery work both in browser and in Electron app.
My web app works fine in browser whereas when I try to load the same url via electron app it throws -

image

Webapp - https://github.com/zulip/zulip
Electron app - https://github.com/zulip/zulip-electron

The webapp is using jquery 1.12.1 version via npm.
I can't use nodeintegration off because I need it in my renderer process.
I'm having hard time solving this . Does anyone know how to make it work?
cc @aaaaahaaaaa @maykefreitas @paulcbetts @tomkel @dkfiresky

@englishextra no luck. I tried to include your code into my app's preload.js but getting same error.

@englishextra No, I don't need jquery in my app. See the update code here. I can't include your code into my app logic since I'm using it only for loading the specified url see this.

I resolved this problem with this

<script type="text/javascript" src="js/jquery.min.js" onload="window.$ = window.jQuery;"></script>

and set

webPreferences: {
            nodeIntegration: false, 
        }

if you with nodeIntegration: true, imorot jQuery like this

<script type="text/javascript" src="js/jquery.min.js" onload="window.$ = window.jQuery = module.exports;"></script>

reference to
https://github.com/electron/electron/issues/345

I mean, it seems to be a bad problem, so why not include it in actual Electron? Maybe integrate major JavaScript libraries into Electron?

Chances are, if you are using jQuery in an Electron App, you are doing something very wrong or at least inefficient.

<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
<script>if (typeof module === 'object') {window.jQuery = window.$ = module.exports;};</script>

@drowlands Including 3party libs in Electron seems to be a bad idea. Imagine the update routine, for instance, or chosing a version.
@amhoho That's because you need to require jQuery, or modify its init code at the top function - leaving only factory( global );

Here:

( function( global, factory ) {

    "use strict";

    if ( typeof module === "object" && typeof module.exports === "object" ) {

        // For CommonJS and CommonJS-like environments where a proper `window`
        // is present, execute the factory and get jQuery.
        // For environments that do not have a `window` with a `document`
        // (such as Node.js), expose a factory as module.exports.
        // This accentuates the need for the creation of a real `window`.
        // e.g. var jQuery = require("jquery")(window);
        // See ticket #14549 for more info.
        module.exports = global.document ?
            factory( global, true ) :
            function( w ) {
                if ( !w.document ) {
                    throw new Error( "jQuery requires a window with a document" );
                }
                return factory( w );
            };
    } else {
        factory( global );
    }

I my case I load libs via fetch/Promise or xhr, so I have this routine to remove module checks in vendors' libs.
@RRorg weird what you're saying

mark

@kabalage I think the configuration in your post above is out of date. It looks like it should be like this now:

const window = new BrowserWindow({
  webPreferences: {
    nodeIntegration: false, 
  },
});

On official docs:
https://electronjs.org/docs/faq#i-can-not-use-jqueryrequirejsmeteorangularjs-in-electron
```



I'm getting
TypeError: Cannot read property 'createElement' of undefined
in webview.

preload script is

const path = require('path');
const {ipcRenderer} = require('electron')
const fs = require('fs');
window.$ = window.jQuery = require(path.join(__dirname, '../jquery.min.js'))

if i use this code, it works fine,
but can't able to close the browserwindow by click of top right button.

Was this page helpful?
0 / 5 - 0 ratings