diff --git a/src/ui/control/navigation_control.js b/src/ui/control/navigation_control.js
index e6b85b9f3d2..58634a9bbfe 100644
--- a/src/ui/control/navigation_control.js
+++ b/src/ui/control/navigation_control.js
@@ -1,13 +1,11 @@
 // @flow
 
 const DOM = require('../../util/dom');
-const window = require('../../util/window');
 const util = require('../../util/util');
+const DragRotateHandler = require('../handler/drag_rotate');
 
 import type Map from '../map';
 
-const className = 'mapboxgl-ctrl';
-
 /**
  * A `NavigationControl` control contains zoom buttons and a compass.
  *
@@ -25,11 +23,20 @@ class NavigationControl {
     _zoomOutButton: HTMLElement;
     _compass: HTMLElement;
     _compassArrow: HTMLElement;
+    _handler: DragRotateHandler;
 
     constructor() {
         util.bindAll([
             '_rotateCompassArrow'
         ], this);
+
+        this._container = DOM.create('div', 'mapboxgl-ctrl mapboxgl-ctrl-group');
+        this._container.addEventListener('contextmenu', (e) => e.preventDefault());
+
+        this._zoomInButton = this._createButton('mapboxgl-ctrl-icon mapboxgl-ctrl-zoom-in', 'Zoom In', () => this._map.zoomIn());
+        this._zoomOutButton = this._createButton('mapboxgl-ctrl-icon mapboxgl-ctrl-zoom-out', 'Zoom Out', () => this._map.zoomOut());
+        this._compass = this._createButton('mapboxgl-ctrl-icon mapboxgl-ctrl-compass', 'Reset North', () => this._map.resetNorth());
+        this._compassArrow = DOM.create('span', 'mapboxgl-ctrl-compass-arrow', this._compass);
     }
 
     _rotateCompassArrow() {
@@ -39,93 +46,29 @@ class NavigationControl {
 
     onAdd(map: Map) {
         this._map = map;
-        this._container = DOM.create('div', `${className} ${className}-group`, map.getContainer());
-        this._container.addEventListener('contextmenu', this._onContextMenu.bind(this));
-
-        this._zoomInButton = this._createButton(`${className}-icon ${className}-zoom-in`, 'Zoom In', map.zoomIn.bind(map));
-        this._zoomOutButton = this._createButton(`${className}-icon ${className}-zoom-out`, 'Zoom Out', map.zoomOut.bind(map));
-        this._compass = this._createButton(`${className}-icon ${className}-compass`, 'Reset North', map.resetNorth.bind(map));
-
-        this._compassArrow = DOM.create('span', `${className}-compass-arrow`, this._compass);
-
-        this._compass.addEventListener('mousedown', this._onCompassDown.bind(this));
-
-        util.bindAll(['_onCompassMove', '_onCompassUp'], this);
-
         this._map.on('rotate', this._rotateCompassArrow);
         this._rotateCompassArrow();
-
+        this._handler = new DragRotateHandler(map, {button: 'left', element: this._compass, pitchWithRotate: false});
+        this._handler.enable();
         return this._container;
     }
 
     onRemove() {
         DOM.remove(this._container);
         this._map.off('rotate', this._rotateCompassArrow);
-        this._map = (undefined: any);
-    }
-
-    _onContextMenu(e: MouseEvent) {
-        e.preventDefault();
-    }
+        delete this._map;
 
-    _onCompassDown(e: MouseEvent) {
-        if (e.button !== 0) return;
-
-        DOM.disableDrag();
-        window.document.addEventListener('mousemove', this._onCompassMove);
-        window.document.addEventListener('mouseup', this._onCompassUp);
-
-        this._map.getCanvasContainer().dispatchEvent(copyMouseEvent(e));
-        e.stopPropagation();
-    }
-
-    _onCompassMove(e: MouseEvent) {
-        if (e.button !== 0) return;
-
-        this._map.getCanvasContainer().dispatchEvent(copyMouseEvent(e));
-        e.stopPropagation();
-    }
-
-    _onCompassUp(e: MouseEvent) {
-        if (e.button !== 0) return;
-
-        window.document.removeEventListener('mousemove', this._onCompassMove);
-        window.document.removeEventListener('mouseup', this._onCompassUp);
-        DOM.enableDrag();
-
-        this._map.getCanvasContainer().dispatchEvent(copyMouseEvent(e));
-        e.stopPropagation();
+        this._handler.disable();
+        delete this._handler;
     }
 
     _createButton(className: string, ariaLabel: string, fn: () => mixed) {
         const a = DOM.create('button', className, this._container);
         a.type = 'button';
         a.setAttribute('aria-label', ariaLabel);
-        a.addEventListener('click', () => { fn(); });
+        a.addEventListener('click', fn);
         return a;
     }
-
 }
 
 module.exports = NavigationControl;
-
-function copyMouseEvent(e) {
-    return new window.MouseEvent(e.type, {
-        button: 2,    // right click
-        buttons: 2,   // right click
-        bubbles: true,
-        cancelable: true,
-        detail: e.detail,
-        view: e.view,
-        screenX: e.screenX,
-        screenY: e.screenY,
-        clientX: e.clientX,
-        clientY: e.clientY,
-        movementX: e.movementX,
-        movementY: e.movementY,
-        ctrlKey: e.ctrlKey,
-        shiftKey: e.shiftKey,
-        altKey: e.altKey,
-        metaKey: e.metaKey
-    });
-}
diff --git a/src/ui/handler/drag_rotate.js b/src/ui/handler/drag_rotate.js
index 34f8b3d46ad..55accf7c5f7 100644
--- a/src/ui/handler/drag_rotate.js
+++ b/src/ui/handler/drag_rotate.js
@@ -27,6 +27,7 @@ class DragRotateHandler {
     _el: HTMLElement;
     _enabled: boolean;
     _active: boolean;
+    _button: 'right' | 'left';
     _bearingSnap: number;
     _pitchWithRotate: boolean;
     _pos: Point;
@@ -34,10 +35,16 @@ class DragRotateHandler {
     _inertia: Array<[number, number]>;
     _center: Point;
 
-    constructor(map: Map, options: any) {
+    constructor(map: Map, options: {
+        button?: 'right' | 'left',
+        element?: HTMLElement,
+        bearingSnap?: number,
+        pitchWithRotate?: boolean
+    }) {
         this._map = map;
-        this._el = map.getCanvasContainer();
-        this._bearingSnap = options.bearingSnap;
+        this._el = options.element || map.getCanvasContainer();
+        this._button = options.button || 'right';
+        this._bearingSnap = options.bearingSnap || 0;
         this._pitchWithRotate = options.pitchWithRotate !== false;
 
         util.bindAll([
@@ -45,7 +52,6 @@ class DragRotateHandler {
             '_onMove',
             '_onUp'
         ], this);
-
     }
 
     /**
@@ -95,16 +101,22 @@ class DragRotateHandler {
         if (this._map.dragPan && this._map.dragPan.isActive()) return;
         if (this.isActive()) return;
 
-        const button = (e.ctrlKey ? 0 : 2);   // ? ctrl+left button : right button
-        let eventButton = e.button;
-        if (typeof window.InstallTrigger !== 'undefined' && e.button === 2 && e.ctrlKey &&
-            window.navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
-            // Fix for https://github.com/mapbox/mapbox-gl-js/issues/3131:
-            // Firefox (detected by InstallTrigger) on Mac determines e.button = 2 when
-            // using Control + left click
-            eventButton = 0;
+        if (this._button === 'right') {
+            const button = (e.ctrlKey ? 0 : 2);   // ? ctrl+left button : right button
+            let eventButton = e.button;
+            if (typeof window.InstallTrigger !== 'undefined' && e.button === 2 && e.ctrlKey &&
+                window.navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
+                // Fix for https://github.com/mapbox/mapbox-gl-js/issues/3131:
+                // Firefox (detected by InstallTrigger) on Mac determines e.button = 2 when
+                // using Control + left click
+                eventButton = 0;
+            }
+            if (eventButton !== button) return;
+        } else {
+            if (e.ctrlKey || e.button !== 0) return;
         }
-        if (eventButton !== button) return;
+
+        DOM.disableDrag();
 
         window.document.addEventListener('mousemove', this._onMove, {capture: true});
         window.document.addEventListener('mouseup', this._onUp);
@@ -162,6 +174,8 @@ class DragRotateHandler {
         window.document.removeEventListener('mouseup', this._onUp);
         window.removeEventListener('blur', (this._onUp: any));
 
+        DOM.enableDrag();
+
         if (!this.isActive()) return;
 
         this._active = false;