hammerjs / hammer.js

A javascript library for multi-touch gestures :// You can touch this
http://hammerjs.github.io
MIT License
24.13k stars 2.62k forks source link

Hammer.js fails in node since window is undefined #930

Open joshunger opened 8 years ago

joshunger commented 8 years ago

Hammer.js fails in node since window is undefined. Also document.

Line 2568 assumes window - https://github.com/hammerjs/hammer.js/blob/master/hammer.js#L2568

We're trying to run a bundle produced by webpack in the web as well as node targets. It would be helpful to include the same bundle even though we're not using hammer.js on the node side.

jQuery does something like -

typeof window !== "undefined" ? window : this

Kishan-Gupta commented 8 years ago

You can fix this by shimming window in Webpack with the imports-loader plugin

require("imports?window=>global!hammer")

Or:

require("imports?window=>{}!hammer")

hburrows commented 8 years ago

Trying to shim this using webpack's imports-loader doesn't work if you're doing server side rendering (like you might do with React). When rendering the initial response on the backend webpack isn't involved so shimming doesn't work. Since hammer is about behavior as opposed to rendering content it would great to fix this limitation. I'm going to submit a PR that takes the jquery approach suggested by @joshunger. I hope you'll consider merging it.

hburrows commented 8 years ago

Unfortunately... it's not quite as simple as passing window or this (global) into the constructor. It also needs document. Not sure this package is setup to be used in a project which renders server side.

arschmitz commented 8 years ago

The only way we could really make this work and what we have discussed to fix this in another issue i cant seem to find is to just noop if there is not a document and window. Hammer cannot operate without them.

hburrows commented 8 years ago

@arschmitz Thanks for the reply. The noop approach is more or less what I was thinking about. Alternatively I could perform a conditional import using a canUseDOM test but I"m using ES6 module imports which don't lend themselves to that. I believe the noop approach would be completely acceptable for SSR since hammer is only adding "behavior" (i.e. events) and not contributing to what's getting rendered.

I'm new to hammer and not really familiar with the codebase. Any tips on how best to noop this? I could add conditionals here and there but that doesn't seem very elegant...

joshunger commented 8 years ago

@arschmitz was it https://github.com/hammerjs/hammer.js/issues/849 Remove global dependency to window and document ?

joshunger commented 8 years ago

Here is the jQuery file I was looking at https://github.com/jquery/jquery/blob/master/src/wrapper.js

arschmitz commented 8 years ago

@joshunger yes thats exactly how it should be done. I'm also a member of the jQuery team :-) the only difference here is how we break up the wrapper we use 2 files beginning and end on hammer

hburrows commented 8 years ago

Submitted PR #973 that fixes this using the jquery style approach suggested by @joshunger. Works well.

andypmw commented 7 years ago

Well, so this project doesn't support server side rendering? 🚶

paranoidjk commented 7 years ago

any progress ?

udivankin commented 7 years ago

For those who use Webpack there's a simple workaround:

plugins: [
  ...
  new webpack.NormalModuleReplacementPlugin(/hammerjs/, 'mocks/HammerMock'),
],

mocks/HammerMock module:

export default class HammerMock {
  constructor() {
    this.DIRECTION_LEFT = 0;
    this.DIRECTION_RIGHT = 1;
  }
  on() {}
}

you will probably need to adjust the code above considering methods you use.

merges commented 7 years ago

@udivankin Thanks for posting the workaround; however, what do you mean by "mocks/HammerMock" module? Is this a module that exists somewhere? Or something one must create? I'm confused.

udivankin commented 7 years ago

@merges that's an ES6 module somewhere in your project (in my case I put it in mocks folder and named HammerMock,js). Webpack's NormalModuleReplacementPlugin will just replace all occurrences of import Hammer from 'hammerjs'; with import Hammer from 'mocks/HammerMock';

graingert commented 7 years ago

you can also use require.extensions to patch out hammerjs.

Tatenda commented 6 years ago

@graingert could u possibly explain further on how to do that

vladp commented 6 years ago

I am also running into this problem. I just started using https://github.com/bytefunc/react-resize-layout, and underneath it is using hammer 2.0.8. My server side rendering config is using webpack 4x. Instructions above not clear, unfortunately. Would somebody be able to offer step-by-step (or gist) on what to do to get this to work in SSR setups? thank you in advance.

Or would it be possible to update Hammer-js such that it does not access window handle within export code? (because that code gets invoked during SSR initialization, but in reality window handle is needed later on (as normally folks that rely on Hammer.JS (eg react-resize-layout), call Hammer in componentDidMount (which happens on client side ).

For completeness, this suggestion did not work for me, because (still a guess), my SSR build/release pipeline is using Razzle (which underneath creates JS chunks (not one big bundle) for efficiency)

olsonpm commented 6 years ago

I had a similar issue with velocity-animate and (crudely) solved it via string-replace-loader

the concept is just

for the ssr entry, replace "import hammerjs from 'hammerjs'\n" with ""

and the loader config

test: /\.js$/,
loader: 'string-replace-loader',
options: {
  search: "import hammerjs from 'hammerjs'\n",
  replace: '',
}

this is working for me in my vue ssr setup currently

justanotherkevin commented 6 years ago

Got a fix. I downloaded the hammer.min.js.
Just wrap the if((typeof document !== 'undefined') && (typeof window !== 'undefined')){ ALL_THE_HAMMER_MIN_JS }

The reason is because Node. When youre in node it does not know what window is. You can test it in youre terminal. Here is the example:


ReferenceError: window is not defined
> if( window ) console.log( 'something' );
ReferenceError: window is not defined
> console.log( typeof window );
undefined
undefined
> if( typeof window === 'undefined' ) console.log( 'this is undefined' );
this is undefined```  
vladp commented 6 years ago

thank you @polymer940c , is there a way to override/polyfill hammer in a way that would not require its source code change in the node_modules? thx again for the suggestion!

adamzhu-xs commented 5 years ago

In case anyone come here for angular ssr issue, you can use below webpack config for your server ts build.

{ test: /hammerjs/, loader: "null-loader" }

Similarly: https://medium.com/@puresmash/solve-hammer-js-issue-on-ssr-project-2e79664a7196

@egjs/hammerjs will work also, but it's not working properly with slider on mobile in my project.

KamilPuczka commented 4 years ago

thank you @polymer940c , is there a way to override/polyfill hammer in a way that would not require its source code change in the node_modules? thx again for the suggestion!

Another solution is to load hammer on demand where you need it, so that you don't have to update hammer.min.js:

if (typeof window !== undefined) { // conditional include because window is undefined on build this.hammer = await import( /* webpackPrefetch: true */ 'hammerjs' ); }

daviddelusenet commented 4 years ago

So this issue is 4 years old and still no solution? 🙄

Brijeshlakkad commented 4 years ago

After installing below 2 dependencies, I was able to launch angular universal.

  1. npm install @egjs/hammerjs --save-dev
  2. npm install @types/hammerjs --save-dev
SahilRajpal-hub commented 2 years ago

Any solution now ??

SahilRajpal-hub commented 1 year ago

I solved this error in my case. Just check if it is browser then only import hammerjs. Basically

const hammerjs = {};
if(this.isBrowser()){
    hammerjs = require('hammer-js');
}

and again check for browser when you are using hammerjs module. It worked