diff --git a/src/ol/control/MousePosition.js b/src/ol/control/MousePosition.js index b36abdc8ee..e46060789a 100644 --- a/src/ol/control/MousePosition.js +++ b/src/ol/control/MousePosition.js @@ -31,7 +31,9 @@ const COORDINATE_FORMAT = 'coordinateFormat'; * callback. * @property {Element|string} [target] Specify a target if you want the * control to be rendered outside of the map's viewport. - * @property {string} [undefinedHTML=''] Markup for undefined coordinates. + * @property {string} [undefinedHTML=''] Markup for undefined coordinates. If + * `undefined`, then the last pointer position is retained when the pointer + * moves outside the viewport. */ @@ -76,7 +78,13 @@ const MousePosition = function(opt_options) { * @private * @type {string} */ - this.undefinedHTML_ = options.undefinedHTML !== undefined ? options.undefinedHTML : ''; + this.undefinedHTML_ = 'undefinedHTML' in options ? options.undefinedHTML : ''; + + /** + * @private + * @type {boolean} + */ + this.renderOnMouseOut_ = this.undefinedHTML_ !== undefined; /** * @private @@ -190,11 +198,13 @@ MousePosition.prototype.setMap = function(map) { if (map) { const viewport = map.getViewport(); this.listenerKeys.push( - listen(viewport, EventType.MOUSEMOVE, - this.handleMouseMove, this), - listen(viewport, EventType.MOUSEOUT, - this.handleMouseOut, this) + listen(viewport, EventType.MOUSEMOVE, this.handleMouseMove, this) ); + if (this.renderOnMouseOut_) { + this.listenerKeys.push( + listen(viewport, EventType.MOUSEOUT, this.handleMouseOut, this) + ); + } } }; @@ -228,7 +238,7 @@ MousePosition.prototype.setProjection = function(projection) { * @private */ MousePosition.prototype.updateHTML_ = function(pixel) { - let html = this.undefinedHTML_; + let html = this.undefinedHTML_ === undefined ? ' ' : this.undefinedHTML_; if (pixel && this.mapProjection_) { if (!this.transform_) { const projection = this.getProjection(); diff --git a/test/spec/ol/control/mouseposition.test.js b/test/spec/ol/control/mouseposition.test.js index d0de8ae158..acd584c8da 100644 --- a/test/spec/ol/control/mouseposition.test.js +++ b/test/spec/ol/control/mouseposition.test.js @@ -1,4 +1,8 @@ +import Map from '../../../../src/ol/Map.js'; import MousePosition from '../../../../src/ol/control/MousePosition.js'; +import View from '../../../../src/ol/View.js'; + +import EventType from '../../../../src/ol/events/EventType.js'; describe('ol.control.MousePosition', function() { @@ -20,4 +24,90 @@ describe('ol.control.MousePosition', function() { }); + describe('configuration options', function() { + let target, map; + const width = 360; + const height = 180; + + beforeEach(function() { + target = document.createElement('div'); + const style = target.style; + style.position = 'absolute'; + style.left = '-1000px'; + style.top = '-1000px'; + style.width = width + 'px'; + style.height = height + 'px'; + + document.body.appendChild(target); + map = new Map({ + target: target, + controls: [], + view: new View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + }); + afterEach(function() { + map.dispose(); + document.body.removeChild(target); + }); + + function simulateEvent(type, x, y) { + const viewport = map.getViewport(); + // calculated in case body has top < 0 (test runner with small window) + const position = viewport.getBoundingClientRect(); + const evt = new MouseEvent(type, { + clientX: position.left + x + width / 2, + clientY: position.top + y + height / 2 + }); + document.querySelector('div.ol-viewport').dispatchEvent(evt); + } + + describe('undefinedHTML', function() { + it('sets div.ol-mouse-position to options.undefinedHTML when mouse moves out', function(done) { + const ctrl = new MousePosition({ + undefinedHTML: 'some text' + }); + ctrl.setMap(map); + map.renderSync(); + + const element = document.querySelector('.ol-mouse-position', map.getTarget()); + expect(element.innerText).to.be('some text'); + + simulateEvent(EventType.MOUSEMOVE, 20, 30); + map.renderSync(); + expect(element.innerText).to.be('20,-30'); + + map.once('postrender', function() { + expect(element.innerText).to.be('some text'); + done(); + }); + simulateEvent(EventType.MOUSEOUT, width + 1, height + 1); + map.renderSync(); + }); + + it('Retain mouse position in div.ol-mouse-position when options.undefinedHTML=undefined and mouse moves outside the viewport', function(done) { + const ctrl = new MousePosition({ + undefinedHTML: undefined + }); + ctrl.setMap(map); + map.renderSync(); + + const element = document.querySelector('.ol-mouse-position', map.getTarget()); + expect(element.innerHTML).to.be(' '); + + target.dispatchEvent(new MouseEvent('mousemove')); + simulateEvent(EventType.MOUSEMOVE, 20, 30); + map.renderSync(); + map.once('postrender', function() { + expect(element.innerText).to.be('20,-30'); + done(); + }); + simulateEvent(EventType.MOUSEOUT, width + 1, height + 1); + map.renderSync(); + }); + }); + }); });