Browserify - How to call function bundled in a file generated through browserify in browser
Asked Answered
G

12

112

I am new to nodejs and browserify. I started with this link .

I have file main.js which contains this code

var unique = require('uniq');

var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];

this.LogData =function(){
console.log(unique(data));
};

Now I Install the uniq module with npm:

 npm install uniq

Then I bundle up all the required modules starting at main.js into a single file called bundle.js with the browserify command:

browserify main.js -o bundle.js

The generated file looks like this:

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var unique = require('uniq');

var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];

this.LogData =function(){
console.log(unique(data));
};

},{"uniq":2}],2:[function(require,module,exports){
"use strict"

function unique_pred(list, compare) {
  var ptr = 1
    , len = list.length
    , a=list[0], b=list[0]
  for(var i=1; i<len; ++i) {
    b = a
    a = list[i]
    if(compare(a, b)) {
      if(i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }
  list.length = ptr
  return list
}

function unique_eq(list) {
  var ptr = 1
    , len = list.length
    , a=list[0], b = list[0]
  for(var i=1; i<len; ++i, b=a) {
    b = a
    a = list[i]
    if(a !== b) {
      if(i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }
  list.length = ptr
  return list
}

function unique(list, compare, sorted) {
  if(list.length === 0) {
    return []
  }
  if(compare) {
    if(!sorted) {
      list.sort(compare)
    }
    return unique_pred(list, compare)
  }
  if(!sorted) {
    list.sort()
  }
  return unique_eq(list)
}

module.exports = unique
},{}]},{},[1])

After including bundle.js file into my index.htm page, how do I call logData function ??

Gamut answered 25/4, 2014 at 14:35 Comment(2)
Where do you want to call it? And why do you want to call it?Nomology
@arturgrzesiak: I want to utilize this function in one of my other project which i will be running in browser.Gamut
M
92

By default, browserify doesn't let you access the modules from outside of the browserified code – if you want to call code in a browserified module, you're supposed to browserify your code together with the module. See http://browserify.org/ for examples of that.

Of course, you could also explicitly make your method accessible from outside like this:

window.LogData =function(){
  console.log(unique(data));
};

Then you could call LogData() from anywhere else on the page.

Manson answered 25/4, 2014 at 14:43 Comment(9)
Thank you. This works. Does this mean, while creating functions instead of saying this.functionName, I should write window.functionName? Do we have any other work around for this? Any reasons for using window.functionName?Gamut
@Brown_Dynamite In a browser, everything you attach to the window object can be accessed from anywhere on the webpage.Manson
Thank you. This makes sense. Is there any work around? otherwise before I browserify my code, I have to add window. in front of all the function i need to expose.Gamut
It is recommended to avoid window contamination like this; specifically because you are binding your memory-based components to a scope that doesn't dispose (or have a reasonable life-cycle).Turbulence
"you're supposed to browserify your code together with the module" - Ugh, what if I want to do something like onclick="someFunction()". You can't possibly be arguing that that's a rare use-case!?!Edmund
@BlueRaja-DannyPflughoeft certainly not rare, but somewhat unclean, especially when you consider that it's not compatible with a secure Content Security PolicyManson
There's a serious lack of documentation anywhere for beginners on how to actually use Browserify on the client.Footlight
yeah, the documentation should clearly state that this is a design decision to be avoided, but provide a clear path to make it work when you don't have an alternative (in my case, using data from the template to populate a JS object)... thanks @Manson for pointing to a simple solution! ;)Puree
I cannot even think of a situation where you WOULDN'T want to make your major functions available outside the module. How is this not default behavior? What kind of web application doesn't call functions?Fula
K
115

The key part of bundling standalone modules with Browserify is the --s option. It exposes whatever you export from your module using node's module.exports as a global variable. The file can then be included in a <script> tag.

You only need to do this if for some reason you need that global variable to be exposed. In my case the client needed a standalone module that could be included in web pages without them needing to worry about this Browserify business.

Here's an example where we use the --s option with an argument of module:

browserify index.js --s module > dist/module.js

This will expose our module as a global variable named module.
Source.

Update: Thanks to @fotinakis. Make sure you're passing --standalone your-module-name. If you forget that --standalone takes an argument, Browserify might silently generate an empty module since it couldn't find it.

Hope this saves you some time.

Krystakrystal answered 2/3, 2015 at 8:42 Comment(7)
I am trying to browserify babelified ES6 code. But the standalone object is empty when I try to console it in browser. Simple ES6 code without any modules works fine in standalone mode. Any pointers on this?Fairman
@jackyrudetsky no idea, I would recommend add a question on SO, sounds like an interesting issue. could be related to this. github.com/substack/node-browserify/issues/1357Krystakrystal
@jackyrudetsky Make sure you're passing --standalone your-module-name. If you forget that --standalone takes an argument, browserify might silently generate an empty module since it couldn't find it.Galcha
@Galcha It was actually an issue in Browserify github.com/substack/node-browserify/issues/1537Fairman
IMO this should be the accepted answer. If you are using a global function, it is much better to have your own namespace than to hang every function off of window.Amentia
@Amentia all global variables in Javascript are elements of window, so both methods achieve the same thing (adding the global variables to window)Sweetsop
If you are using watchify to re-bundle each time your code changes, you can use the --s modulename options with it as well, i.e.: watchify test~/bundletest.js --s BUNDY -o test~/bundled_bundletest.js -v. Here my module is named BUNDY.Kwangchowan
M
92

By default, browserify doesn't let you access the modules from outside of the browserified code – if you want to call code in a browserified module, you're supposed to browserify your code together with the module. See http://browserify.org/ for examples of that.

Of course, you could also explicitly make your method accessible from outside like this:

window.LogData =function(){
  console.log(unique(data));
};

Then you could call LogData() from anywhere else on the page.

Manson answered 25/4, 2014 at 14:43 Comment(9)
Thank you. This works. Does this mean, while creating functions instead of saying this.functionName, I should write window.functionName? Do we have any other work around for this? Any reasons for using window.functionName?Gamut
@Brown_Dynamite In a browser, everything you attach to the window object can be accessed from anywhere on the webpage.Manson
Thank you. This makes sense. Is there any work around? otherwise before I browserify my code, I have to add window. in front of all the function i need to expose.Gamut
It is recommended to avoid window contamination like this; specifically because you are binding your memory-based components to a scope that doesn't dispose (or have a reasonable life-cycle).Turbulence
"you're supposed to browserify your code together with the module" - Ugh, what if I want to do something like onclick="someFunction()". You can't possibly be arguing that that's a rare use-case!?!Edmund
@BlueRaja-DannyPflughoeft certainly not rare, but somewhat unclean, especially when you consider that it's not compatible with a secure Content Security PolicyManson
There's a serious lack of documentation anywhere for beginners on how to actually use Browserify on the client.Footlight
yeah, the documentation should clearly state that this is a design decision to be avoided, but provide a clear path to make it work when you don't have an alternative (in my case, using data from the template to populate a JS object)... thanks @Manson for pointing to a simple solution! ;)Puree
I cannot even think of a situation where you WOULDN'T want to make your major functions available outside the module. How is this not default behavior? What kind of web application doesn't call functions?Fula
G
57

@Matas Vaitkevicius's answer with Browserify's standalone option is correct (@thejh's answer using the window global variable also works, but as others have noted, it pollutes the global namespace so it's not ideal). I wanted to add a little more detail on how to use the standalone option.

In the source script that you want to bundle, make sure to expose the functions you want to call via module.exports. In the client script, you can call these exposed functions via <bundle-name>.<func-name>. Here's an example:

My source file src/script.js will have this:
module.exports = {myFunc: func};

My browserify command will look something like this:
browserify src/script.js --standalone myBundle > dist/bundle.js

And my client script dist/client.js will load the bundled script
<script src="bundle.js"></script>
and then call the exposed function like this:
<script>myBundle.myFunc();</script>


There's no need to require the bundle name in the client script before calling the exposed functions, e.g. <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script> isn't necessary and won't work.

In fact, just like all functions bundled by browserify without standalone mode, the require function won't be available outside of the bundled script. Browserify allows you to use some Node functions client-side, but only in the bundled script itself; it's not meant to create a standalone module you can import and use anywhere client-side, which is why we have to go to all this extra trouble just to call a single function outside of its bundled context.

Giglio answered 4/4, 2017 at 19:22 Comment(5)
Wow! Finally a practical example.Sochi
Good example, but as far as "it pollutes the global namespace therefore not ideal" does not follow automatically, it may be acceptable if it's only one function; Just smoke and mirrors, even myBundle gets attached to the window object, window.myBundle.myFunc() instead of window.myFunc()Shillelagh
There should be extra points for people who give end-to-end examples.Dangerous
That is how documentation should be writtenAngellaangelle
Not working at my side, this is my post.Rollback
C
8

I just read through the answers and seems like nobody mentioned the use of the global variable scope? Which is usefull if you want to use the same code in node.js and in the browser.

class Test
{
  constructor()
  {
  }
}
global.TestClass = Test;

Then you can access the TestClass anywhere.

<script src="bundle.js"></script>
<script>
var test = new TestClass(); // Enjoy!
</script>

Note: The TestClass then becomes available everywhere. Which is the same as using the window variable.

Additionally you can create a decorator that exposes a class to the global scope. Which is really nice but makes it hard to track where a variable is defined.

Concerned answered 13/5, 2017 at 2:44 Comment(13)
As you yourself say, adding the function to global produces the same effect as adding to window, which was already covered by thejh. This answer adds no new information.Giglio
@GalenLong maybe you forgot that there is no window variable in node.js? And some libraries that target node and browser may want to use global instead. My answer got a few upvotes and is not in minus yet so i think its informative for others if not for you.Concerned
You're right, @Azarus. There were two other duplicate answers on the page and I incorrectly included yours in with the bunch. My apologies.Giglio
just want to note that the hanging parens here is a very bad practice for javascript, for example: apply this pattern to the return keyword and prepare to cry. e.g return {} but drop the opening curly brace down to the next line.Equinox
@Equinox i have no clue what are you talking about sorry. Can you elaborate?Concerned
@Azarus I created a fiddle to demonstrate what I mean - jsfiddle.net/cubaksot/1Equinox
I can only say that not knowing the syntax of javascript and keeping braces in a new line has 0 to do with bad practice.. braces in new line actually make code more readable imho. But it's up to you how you write your code and it has nothing to do with the quality of the answer. It's really tabs or spacesConcerned
@Azarus except in this case it isn't a preference, in my jsfiddle example case it breaks code, I mean, that it produces unexpected results. With "Spaces vs. tabs" argument, at least your code isn't broken. If you drop the opening parenthesis to the next line below the return keyword statement, your object (or array, or any opening token...) will NOT get returned out of the function. this is an issue specifically how Javascript and the return keyword works with this style of code formatting.Equinox
@Azarus Actually, if you revisit the jsfiddle now there is a linter in-place and it's calling out the line break after the return statement.Equinox
You don't put braces in a new line for a return statement? And you can put braces in a new line after a function definition?Concerned
You're arguing... Please stop bothering with this it adds no value to the answerConcerned
@Azarus, how to "create a decorator that exposes a class" ? as per last part of your answer.Shillelagh
have a look at typescript decorators it's fairly simple. Or create a question and i'll answer there with code samples. The channel here is just not right.Concerned
C
6

Minimal runnable example

This is basically the same as: https://mcmap.net/q/193839/-browserify-how-to-call-function-bundled-in-a-file-generated-through-browserify-in-browser but with concrete files that will allow you to just run and easily reproduce it yourself.

This code is also available at: https://github.com/cirosantilli/browserify-hello-world

index.js

const uniq = require('uniq');

function myfunc() {
  return uniq([1, 2, 2, 3]).join(' ');
}
exports.myfunc = myfunc;

index.html

<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>Browserify hello world</title>
</head>
<body>
<div id="container">
</body>
</div>
<script src="out.js"></script>
<script>
document.getElementById('container').innerHTML = browserify_hello_world.myfunc();
</script>
</html>

Node.js usage:

#!/usr/bin/env node

const browserify_hello_world = require('./index.js');

console.log(browserify_hello_world.myfunc());

Generate out.js for browser usage:

npx browserify --outfile out.js --standalone browserify_hello_world index.js

Both the browser and the command line show the expected output:

1 2 3

Tested with Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10.

Corydalis answered 17/12, 2019 at 9:21 Comment(1)
The exports.myfunc.= myfunc portion of this was absolutely critical and missed in other answers.Playmate
I
5

Read README.md of browserify about --standalone parameter or google "browserify umd"

Incumbency answered 1/7, 2014 at 16:35 Comment(2)
This is more a hint on where to find an answer than an answer.Igenia
this lead me to the solution I was looking for for two days (how to use browserify output from a require.js environment). thank you!Oral
P
3

Whole concept is about wrapping.

1.) Alternative - Object "this"

for this purpose I'll assume you have "only 1 script for whole app {{app_name}}" and "1 function {{function_name}}"

add function {{function_name}}

function {{function_name}}(param) { ... }

to object this

this.{{function_name}} = function(param) { ... }

then you have to name that object to be available - you will do it add param "standalone with name" like others advised

so if you use "watchify" with "browserify" use this

var b = browserify({
    ...
    standalone: '{{app_name}}'
});

or command line

browserify index.js --standalone {{app_name}} > index-bundle.js

then you can call the function directly

{{app_name}}.{{function_name}}(param);
window.{{app_name}}.{{function_name}}(param);

2.) Alternative - Object "window"

add function {{function_name}}

function {{function_name}}(param) { ... }

to object window

window.{{function_name}} = function(param) { ... }

then you can call the function directly

{{function_name}}(param);
window.{{function_name}}(param);
Proxy answered 22/1, 2019 at 12:12 Comment(0)
R
3

To have your function available from both the HTML and from server-side node:

main.js:

var unique = require('uniq');

function myFunction() {
    var data = [1, 2, 2, 4, 3];
    return unique(data).toString();
}
console.log ( myFunction() );

// When browserified - we can't call myFunction() from the HTML, so we'll externalize myExtFunction()
// On the server-side "window" is undef. so we hide it.
if (typeof window !== 'undefined') {
    window.myExtFunction = function() {
        return myFunction();
    }
}

main.html:

<html>
    <head>
        <script type='text/javascript' src="bundle.js"></script>
    <head>
    <body>
        Result: <span id="demo"></span>
        <script>document.getElementById("demo").innerHTML = myExtFunction();</script>
    </body>
</html>

Run:

npm install uniq
browserify main.js > bundle.js

and you should get same results when opening main.html in a browser as when running

node main.js
Roseline answered 8/8, 2019 at 17:24 Comment(0)
T
1

You have a few options:

  1. Let plugin browserify-bridge auto-export the modules to a generated entry module. This is helpful for SDK projects or situations where you don't have to manually keep up with what is exported.

  2. Follow a pseudo-namespace pattern for roll-up exposure:

First, arrange your library like this, taking advantage of index look-ups on folders:

/src
--entry.js
--/helpers
--- index.js
--- someHelper.js
--/providers
--- index.js
--- someProvider.js
...

With this pattern, you define entry like this:

exports.Helpers = require('./helpers');
exports.Providers = require('./providers');
...

Notice the require automatically loads the index.js from each respective sub-folder

In your subfolders, you can just include a similar manifest of the available modules in that context:

exports.SomeHelper = require('./someHelper');

This pattern scales really well and allows for contextual (folder by folder) tracking of what to include in the rolled-up api.

Turbulence answered 12/8, 2015 at 21:6 Comment(0)
H
0

You can also call your function from the html file like this:

main.js: (will be in bundle.js)

window.onload = function () {
    document.getElementById('build-file')
        .addEventListener('click', buildFile)

}
function buildFile() {
 ...
}

index.html:

<button id="build-file"">Build file</button>
Heterosexuality answered 30/12, 2020 at 22:46 Comment(0)
L
-1
window.LogData =function(data){
   return unique(data);
};

Call the function simply by LogData(data)

This is just a slight modification to thejh's answer but important one

Lamellirostral answered 1/5, 2018 at 8:56 Comment(1)
This modification is irrelevant to the question asker's concerns and doesn't add any new information given the already existing answers.Giglio
F
-2

For debugging purposes I added this line to my code.js:

window.e = function(data) {eval(data);};

Then I could run anything even outside the bundle.

e("anything();");
Falzetta answered 23/7, 2017 at 22:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.