svgmap 2.19.3 → 2.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -99,7 +99,11 @@ You can pass the following options into svgMap:
99
99
  | `hideFlag` | `boolean` | `false` | Hide the flag in tooltips |
100
100
  | `noDataText` | `string` | `'No data available'` | The text to be shown when no data is present |
101
101
  | `touchLink` | `boolean` | `false` | Set to `true` to open the link (see `data.values.link`) on mobile devices, by default the tooltip will be shown |
102
+ | `showTooltips` | `boolean` | `true` | When `false`, disables hover and touch-following tooltips only. Persistent on-map labels from `persistentTooltips` are unaffected. On touch devices, countries with a `link` open it on the first tap instead of using the two-tap pattern (first tap preview, second tap navigate). |
103
+ | `tooltipTrigger` | `'hover'`, `'click'` | `'hover'` | How the floating tooltip opens with the **mouse**: `'hover'` opens on mouseenter/mouseleave.`'click'` opens on primary click and closes when clicking outside the map countries or tooltip. Only applies when `showTooltips` is `true`. |
104
+ | `persistentTooltips` | `false`, `array`, `function` | `false` | Persistent tooltips fixed on the map when it loads: an array of country IDs, or a function (`function (countryID, countryValues) { … }`) to decide per country. Independent of `showTooltips`. Best used with `showTooltips: false` or `tooltipTrigger: 'click'`. |
102
105
  | `onGetTooltip` | `function` | | Called when a tooltip is created to custimize the tooltip content (`function (tooltipDiv, countryID, countryValues) { return 'Custom HTML'; }`) |
106
+ | `onCountryClick` | `function` | | Called when the user clicks a country (primary button, pointer released without dragging). Signature: `function (countryID, event) { … }`. Use this for custom actions instead of or in addition to `data.values.link`. Return `false` to skip opening the URL when the country has a `link`. On touch devices with a link, the callback runs when the tap would navigate (not on the first tap that only shows the tooltip). Countries show a pointer cursor while this option is set. |
103
107
  | `countries` | `object` | | Additional options specific to countries: |
104
108
  |    `↳ EH` | `boolean` | `true` | When set to `false`, Western Sahara (EH) will be combined with Morocco (MA) |
105
109
  | `data` | `object` | | The chart data to use for coloring and to show in the tooltip. Use a unique data-id as key and provide following options as value: |
package/dist/index.cjs CHANGED
@@ -2344,6 +2344,15 @@ class svgMap {
2344
2344
  // Set to true to open the link on mobile devices, set to false (default) to show the tooltip
2345
2345
  touchLink: false,
2346
2346
 
2347
+ // When false, disables hover/touch-following tooltips (not the on-map persistent labels; see persistentTooltips)
2348
+ showTooltips: true,
2349
+
2350
+ // 'hover' (default): mouse shows tooltip on enter. 'click': mouse opens tooltip on click; touch/pen unchanged.
2351
+ tooltipTrigger: 'hover',
2352
+
2353
+ // Persistent on-map tooltips: an array of country IDs, or a function (countryID, countryValues) => boolean
2354
+ persistentTooltips: false,
2355
+
2347
2356
  // Set to true to show the to show a zoom reset button
2348
2357
  showZoomReset: false,
2349
2358
 
@@ -2352,6 +2361,10 @@ class svgMap {
2352
2361
  return null;
2353
2362
  },
2354
2363
 
2364
+ // Called on country click (pointer released without dragging). Receives
2365
+ // (countryID, event). Return false to skip opening data.values[*].link.
2366
+ onCountryClick: null,
2367
+
2355
2368
  // Country specific options
2356
2369
  countries: {
2357
2370
  // Western Sahara: Set to false to combine Morocco (MA) and Western Sahara (EH)
@@ -2391,6 +2404,9 @@ class svgMap {
2391
2404
  // Wrapper element
2392
2405
  this.wrapper = document.getElementById(this.options.targetElementID);
2393
2406
  this.wrapper.classList.add('svgMap-wrapper');
2407
+ if (typeof this.options.onCountryClick === 'function') {
2408
+ this.wrapper.classList.add('svgMap-country-click-callback');
2409
+ }
2394
2410
 
2395
2411
  // Container element
2396
2412
  this.container = document.createElement('div');
@@ -3082,7 +3098,9 @@ class svgMap {
3082
3098
 
3083
3099
  createMap() {
3084
3100
  // Create the tooltip
3085
- this.createTooltip();
3101
+ if (this.options.showTooltips) {
3102
+ this.createTooltip();
3103
+ }
3086
3104
 
3087
3105
  // Create map wrappers
3088
3106
  this.mapWrapper = this.createElement(
@@ -3208,6 +3226,103 @@ class svgMap {
3208
3226
  }.bind(this);
3209
3227
 
3210
3228
  // Add map elements
3229
+ var countryElements = [];
3230
+
3231
+ const clearActive = function clearActive() {
3232
+ this.mapImage
3233
+ .querySelectorAll('.svgMap-active')
3234
+ .forEach((el) => el.classList.remove('svgMap-active'));
3235
+ }.bind(this);
3236
+
3237
+ const isClickTooltip =
3238
+ this.options.showTooltips && this.options.tooltipTrigger === 'click';
3239
+
3240
+ const getCountryFromEvent = function (e) {
3241
+ return e.target && e.target.closest
3242
+ ? e.target.closest('.svgMap-country')
3243
+ : null;
3244
+ };
3245
+
3246
+ const raiseCountry = function (countryElement, setActive) {
3247
+ if (setActive) {
3248
+ clearActive();
3249
+ }
3250
+ countryElement.parentNode.insertBefore(
3251
+ countryElement,
3252
+ this.persistentTooltipGroup || null
3253
+ );
3254
+ if (setActive) {
3255
+ countryElement.classList.add('svgMap-active');
3256
+ }
3257
+ }.bind(this);
3258
+
3259
+ const showCountryTooltip = function (countryElement, e, setActive) {
3260
+ raiseCountry(countryElement, setActive);
3261
+ this.setTooltipContent(this.getTooltipContent(countryElement.dataset.id));
3262
+ this.showTooltip(e);
3263
+ }.bind(this);
3264
+
3265
+ // Touch only: preview tooltip on finger down without marking country active
3266
+ // (active is set on pointerup so link countries keep two-tap navigation)
3267
+ this.mapImage.addEventListener(
3268
+ 'pointerdown',
3269
+ (e) => {
3270
+ if (!this.options.showTooltips || e.pointerType !== 'touch') {
3271
+ return;
3272
+ }
3273
+
3274
+ const countryElement = getCountryFromEvent(e);
3275
+ if (!countryElement) {
3276
+ this.hideTooltip();
3277
+ return;
3278
+ }
3279
+
3280
+ showCountryTooltip(countryElement, e, false);
3281
+ this.moveTooltip(e);
3282
+ },
3283
+ { passive: true }
3284
+ );
3285
+
3286
+ this.mapImage.addEventListener(
3287
+ 'pointercancel',
3288
+ (e) => {
3289
+ if (e.pointerType === 'touch') {
3290
+ this.hideTooltip();
3291
+ }
3292
+ },
3293
+ { passive: true }
3294
+ );
3295
+
3296
+ // Hover (mouse/pen) and touch drag: raise country + optional floating tooltip
3297
+ this.mapImage.addEventListener(
3298
+ 'pointermove',
3299
+ (e) => {
3300
+ const countryElement = getCountryFromEvent(e);
3301
+ if (!countryElement) {
3302
+ clearActive();
3303
+ if (this.options.showTooltips) {
3304
+ this.hideTooltip();
3305
+ }
3306
+ return;
3307
+ }
3308
+
3309
+ const mouseClickMode = e.pointerType === 'mouse' && isClickTooltip;
3310
+
3311
+ // Always raise hovered country (SVG paint order + .svgMap-active stroke)
3312
+ if (!this.options.showTooltips || mouseClickMode) {
3313
+ raiseCountry(countryElement, true);
3314
+ return;
3315
+ }
3316
+
3317
+ showCountryTooltip(countryElement, e, true);
3318
+
3319
+ if (e.pointerType === 'touch') {
3320
+ this.moveTooltip(e);
3321
+ }
3322
+ },
3323
+ { passive: true }
3324
+ );
3325
+
3211
3326
  Object.keys(mapPaths).forEach(
3212
3327
  function (countryID) {
3213
3328
  var countryData = this.mapPaths[countryID];
@@ -3225,156 +3340,50 @@ class svgMap {
3225
3340
  'id',
3226
3341
  this.id + '-map-country-' + countryID
3227
3342
  );
3228
- countryElement.setAttribute('data-id', countryID);
3343
+ countryElement.dataset.id = countryID;
3229
3344
  countryElement.classList.add('svgMap-country');
3230
3345
 
3231
3346
  this.mapImage.appendChild(countryElement);
3232
-
3233
- // Add tooltip when touch is used
3234
- function handlePointerMove(e) {
3235
- if (e.pointerType === 'touch') return;
3236
-
3237
- const target = document.elementFromPoint(e.clientX, e.clientY);
3238
-
3239
- if (
3240
- !target ||
3241
- (!target.closest('.svgMap-country') &&
3242
- !target.closest('.svgMap-tooltip'))
3243
- ) {
3244
- this.hideTooltip();
3245
- document
3246
- .querySelectorAll('.svgMap-active')
3247
- .forEach(el => el.classList.remove('svgMap-active'));
3248
- }
3249
- }
3250
-
3251
- const handlePointerMoveBound = handlePointerMove.bind(this);
3252
-
3253
- countryElement.addEventListener(
3254
- 'pointerenter',
3255
- function (e) {
3256
- // Only add pointermove listener for non-touch pointers
3257
- if (e.pointerType !== 'touch') {
3258
- document.addEventListener(
3259
- 'pointermove',
3260
- handlePointerMoveBound,
3261
- { passive: true }
3262
- );
3263
- }
3264
-
3265
- document
3266
- .querySelectorAll('.svgMap-active')
3267
- .forEach(el => el.classList.remove('svgMap-active'));
3268
-
3269
- countryElement.parentNode.appendChild(countryElement);
3270
- countryElement.classList.add('svgMap-active');
3271
-
3272
- const countryID = countryElement.getAttribute('data-id');
3273
- this.setTooltipContent(this.getTooltipContent(countryID));
3274
- this.showTooltip(e);
3275
-
3276
- // For touch, move tooltip to the touch position and keep it there
3277
- if (e.pointerType === 'touch') {
3278
- this.moveTooltip(e);
3279
- }
3280
- }.bind(this)
3281
- );
3282
-
3283
- // Handle touch move - update tooltip position while panning
3284
- countryElement.addEventListener(
3285
- 'touchmove',
3286
- function (e) {
3287
- this.moveTooltip(e);
3288
- }.bind(this),
3289
- { passive: true }
3290
- );
3291
-
3292
- // Handle touch end - remove active state and hide tooltip
3293
- countryElement.addEventListener(
3294
- 'touchend',
3295
- function (e) {
3296
- const touch = e.changedTouches[0];
3297
- const elementAtEnd = document.elementFromPoint(touch.clientX, touch.clientY);
3298
-
3299
- // Only hide if touch ended outside the country or tooltip
3300
- if (
3301
- !elementAtEnd ||
3302
- (!elementAtEnd.closest('.svgMap-country') &&
3303
- !elementAtEnd.closest('.svgMap-tooltip'))
3304
- ) {
3305
- this.hideTooltip();
3306
- document
3307
- .querySelectorAll('.svgMap-active')
3308
- .forEach(el => el.classList.remove('svgMap-active'));
3309
- }
3310
- }.bind(this),
3311
- { passive: true }
3312
- );
3313
-
3314
- // Remove pointermove listener when leaving non-touch pointer
3315
- countryElement.addEventListener(
3316
- 'pointerleave',
3317
- function (e) {
3318
- if (e.pointerType !== 'touch') {
3319
- document.removeEventListener('pointermove', handlePointerMoveBound);
3320
- this.hideTooltip();
3321
- document
3322
- .querySelectorAll('.svgMap-active')
3323
- .forEach(el => el.classList.remove('svgMap-active'));
3324
- }
3325
- }.bind(this)
3326
- );
3327
-
3328
- document.addEventListener(
3329
- 'pointerover',
3330
- function (e) {
3331
- if (e.pointerType !== 'touch') return;
3332
-
3333
- if (
3334
- e.target.closest('.svgMap-country') ||
3335
- e.target.closest('.svgMap-tooltip')
3336
- ) {
3337
- return;
3338
- }
3339
-
3340
- this.hideTooltip();
3341
- document
3342
- .querySelectorAll('.svgMap-active')
3343
- .forEach(el => el.classList.remove('svgMap-active'));
3344
- }.bind(this),
3345
- { passive: true }
3346
- );
3347
+ countryElements.push(countryElement);
3347
3348
 
3348
3349
  if (
3349
3350
  this.options.data.values &&
3350
3351
  this.options.data.values[countryID] &&
3351
3352
  this.options.data.values[countryID]['link']
3352
3353
  ) {
3353
- countryElement.setAttribute(
3354
- 'data-link',
3355
- this.options.data.values[countryID]['link']
3356
- );
3354
+ countryElement.dataset.link =
3355
+ this.options.data.values[countryID]['link'];
3357
3356
  if (this.options.data.values[countryID]['linkTarget']) {
3358
- countryElement.setAttribute(
3359
- 'data-link-target',
3360
- this.options.data.values[countryID]['linkTarget']
3361
- );
3357
+ countryElement.dataset.linkTarget =
3358
+ this.options.data.values[countryID]['linkTarget'];
3362
3359
  }
3363
3360
  }
3364
3361
  }.bind(this)
3365
3362
  );
3366
3363
 
3364
+ var persistent = this.options.persistentTooltips;
3365
+ if (
3366
+ persistent &&
3367
+ (Array.isArray(persistent) || typeof persistent === 'function')
3368
+ ) {
3369
+ this.createPersistentTooltips(countryElements);
3370
+ }
3371
+
3367
3372
  let pointerStart = null;
3368
3373
  let activeCountry = null;
3369
3374
 
3370
- this.mapImage.addEventListener('pointerdown', e => {
3371
- // Ignore right click (on desktop it allows inspecting the chart elements without opening the URL)
3372
- if (e.button !== 0) return;
3375
+ this.mapImage.addEventListener(
3376
+ 'pointerdown',
3377
+ (e) => {
3378
+ // Ignore right click (on desktop it allows inspecting the chart elements without opening the URL)
3379
+ if (e.button !== 0) return;
3373
3380
 
3374
- pointerStart = { x: e.clientX, y: e.clientY };
3375
- }, { passive: true });
3381
+ pointerStart = { x: e.clientX, y: e.clientY };
3382
+ },
3383
+ { passive: true }
3384
+ );
3376
3385
 
3377
- this.mapImage.addEventListener('pointerup', e => {
3386
+ this.mapImage.addEventListener('pointerup', (e) => {
3378
3387
  // Ignore right click (on desktop it allows inspecting the chart elements without opening the URL)
3379
3388
  if (e.button !== 0) return;
3380
3389
 
@@ -3392,33 +3401,121 @@ class svgMap {
3392
3401
  const countryElement = e.target.closest('.svgMap-country');
3393
3402
  if (!countryElement) return;
3394
3403
 
3395
- const countryID = countryElement.getAttribute('data-id');
3396
- const link = countryElement.getAttribute('data-link');
3397
- const target = countryElement.getAttribute('data-link-target');
3398
- if (!link) return;
3399
-
3404
+ const countryID = countryElement.dataset.id;
3405
+ const link = countryElement.dataset.link;
3406
+ const linkTarget = countryElement.dataset.linkTarget;
3407
+ const hasCallback = typeof this.options.onCountryClick === 'function';
3408
+ const hasLink = !!link;
3400
3409
  const isTouch = e.pointerType === 'touch' || e.pointerType === 'pen';
3401
3410
 
3411
+ const isClickTooltipMouse = e.pointerType === 'mouse' && isClickTooltip;
3412
+
3413
+ if (!hasLink && !hasCallback && !isClickTooltipMouse) return;
3414
+
3415
+ if (isClickTooltipMouse) {
3416
+ const willNavigate =
3417
+ hasLink && countryElement.classList.contains('svgMap-active');
3418
+ const shouldFireCallback = hasCallback && (!hasLink || willNavigate);
3419
+
3420
+ var callbackResultClick;
3421
+ if (shouldFireCallback) {
3422
+ callbackResultClick = this.options.onCountryClick(countryID, e);
3423
+ }
3424
+
3425
+ if (hasLink) {
3426
+ if (callbackResultClick === false) return;
3427
+ if (countryElement.classList.contains('svgMap-active')) {
3428
+ if (linkTarget) window.open(link, linkTarget);
3429
+ else window.location.href = link;
3430
+ } else {
3431
+ clearActive();
3432
+ countryElement.parentNode.insertBefore(
3433
+ countryElement,
3434
+ this.persistentTooltipGroup || null
3435
+ );
3436
+ countryElement.classList.add('svgMap-active');
3437
+ this.setTooltipContent(this.getTooltipContent(countryID));
3438
+ this.showTooltip(e);
3439
+ }
3440
+ return;
3441
+ }
3442
+
3443
+ if (callbackResultClick === false) return;
3444
+
3445
+ clearActive();
3446
+ countryElement.parentNode.insertBefore(
3447
+ countryElement,
3448
+ this.persistentTooltipGroup || null
3449
+ );
3450
+ countryElement.classList.add('svgMap-active');
3451
+ this.setTooltipContent(this.getTooltipContent(countryID));
3452
+ this.showTooltip(e);
3453
+ return;
3454
+ }
3455
+
3456
+ const willNavigate =
3457
+ hasLink &&
3458
+ (!isTouch || countryElement.classList.contains('svgMap-active'));
3459
+
3460
+ const shouldFireCallback =
3461
+ hasCallback && (!hasLink || !isTouch || willNavigate);
3462
+
3463
+ var callbackResult;
3464
+ if (shouldFireCallback) {
3465
+ callbackResult = this.options.onCountryClick(countryID, e);
3466
+ }
3467
+
3468
+ if (!hasLink) return;
3469
+
3470
+ if (callbackResult === false) return;
3471
+
3402
3472
  if (isTouch) {
3403
3473
  // Touch: only open if already active
3404
3474
  if (countryElement.classList.contains('svgMap-active')) {
3405
- if (target) window.open(link, target);
3475
+ if (linkTarget) window.open(link, linkTarget);
3406
3476
  else window.location.href = link;
3407
3477
  } else {
3408
- // first tap shows tooltip
3478
+ // first tap shows tooltip (or opens link immediately if tooltips are off)
3409
3479
  if (activeCountry) activeCountry.classList.remove('svgMap-active');
3410
3480
  activeCountry = countryElement;
3411
3481
  countryElement.classList.add('svgMap-active');
3412
- this.setTooltipContent(this.getTooltipContent(countryID));
3413
- this.showTooltip(e);
3482
+ if (this.options.showTooltips) {
3483
+ this.setTooltipContent(this.getTooltipContent(countryID));
3484
+ this.showTooltip(e);
3485
+ } else {
3486
+ if (linkTarget) window.open(link, linkTarget);
3487
+ else window.location.href = link;
3488
+ }
3414
3489
  }
3415
3490
  } else {
3416
3491
  // Desktop: open immediately
3417
- if (target) window.open(link, target);
3492
+ if (linkTarget) window.open(link, linkTarget);
3418
3493
  else window.location.href = link;
3419
3494
  }
3420
3495
  });
3421
3496
 
3497
+ this._clickTooltipOutsideHandler = function (ev) {
3498
+ if (!this.tooltip || !this.tooltip.classList.contains('svgMap-active')) {
3499
+ return;
3500
+ }
3501
+ var node = ev.target;
3502
+ if (
3503
+ node &&
3504
+ node.closest &&
3505
+ (node.closest('.svgMap-country') || node.closest('.svgMap-tooltip'))
3506
+ ) {
3507
+ return;
3508
+ }
3509
+ this.hideTooltip();
3510
+ if (this.mapImage) {
3511
+ this.mapImage
3512
+ .querySelectorAll('.svgMap-country.svgMap-active')
3513
+ .forEach(function (el) {
3514
+ el.classList.remove('svgMap-active');
3515
+ });
3516
+ }
3517
+ }.bind(this);
3518
+
3422
3519
  // Expose instance
3423
3520
  var me = this;
3424
3521
 
@@ -3484,13 +3581,82 @@ class svgMap {
3484
3581
  }
3485
3582
  }
3486
3583
 
3584
+ // Create the persistent tooltips
3585
+
3586
+ createPersistentTooltips(countryElements) {
3587
+ if (this.persistentTooltipGroup) {
3588
+ this.persistentTooltipGroup.remove();
3589
+ }
3590
+
3591
+ this.persistentTooltipGroup = document.createElementNS(
3592
+ 'http://www.w3.org/2000/svg',
3593
+ 'g'
3594
+ );
3595
+ this.persistentTooltipGroup.classList.add('svgMap-persistent-tooltips');
3596
+ this.mapImage.appendChild(this.persistentTooltipGroup);
3597
+
3598
+ countryElements.forEach(
3599
+ function (countryElement) {
3600
+ var countryID = countryElement.dataset.id;
3601
+ if (!this.shouldShowTooltipOnLoad(countryID)) {
3602
+ return;
3603
+ }
3604
+
3605
+ var boundingBox = countryElement.getBBox();
3606
+ var tooltipPosition = {
3607
+ x: boundingBox.x + boundingBox.width / 2,
3608
+ y: boundingBox.y + boundingBox.height / 2
3609
+ };
3610
+
3611
+ var tooltipObject = document.createElementNS(
3612
+ 'http://www.w3.org/2000/svg',
3613
+ 'foreignObject'
3614
+ );
3615
+ tooltipObject.setAttribute('x', tooltipPosition.x);
3616
+ tooltipObject.setAttribute('y', tooltipPosition.y);
3617
+ tooltipObject.setAttribute('width', 1);
3618
+ tooltipObject.setAttribute('height', 1);
3619
+ tooltipObject.classList.add('svgMap-persistent-tooltip-wrapper');
3620
+
3621
+ var tooltipElement = this.createElement(
3622
+ 'div',
3623
+ 'svgMap-persistent-tooltip',
3624
+ tooltipObject
3625
+ );
3626
+ tooltipElement.append(
3627
+ this.getTooltipContent(countryID, tooltipElement)
3628
+ );
3629
+ this.createElement('div', 'svgMap-tooltip-pointer', tooltipElement);
3630
+
3631
+ this.persistentTooltipGroup.appendChild(tooltipObject);
3632
+ }.bind(this)
3633
+ );
3634
+ }
3635
+
3636
+ // Check if a persistent tooltip should be shown on load
3637
+
3638
+ shouldShowTooltipOnLoad(countryID) {
3639
+ var persistent = this.options.persistentTooltips;
3640
+ var countryValues = this.options.data.values[countryID];
3641
+
3642
+ if (Array.isArray(persistent)) {
3643
+ return persistent.indexOf(countryID) !== -1;
3644
+ }
3645
+
3646
+ if (typeof persistent === 'function') {
3647
+ return persistent(countryID, countryValues);
3648
+ }
3649
+
3650
+ return false;
3651
+ }
3652
+
3487
3653
  // Create the tooltip content
3488
3654
 
3489
- getTooltipContent(countryID) {
3655
+ getTooltipContent(countryID, tooltipDiv = this.tooltip) {
3490
3656
  // Custom tooltip
3491
3657
  if (this.options.onGetTooltip) {
3492
3658
  var customDiv = this.options.onGetTooltip(
3493
- this.tooltip,
3659
+ tooltipDiv,
3494
3660
  countryID,
3495
3661
  this.options.data.values[countryID]
3496
3662
  );
@@ -4472,19 +4638,50 @@ class svgMap {
4472
4638
  // Show the tooltip
4473
4639
 
4474
4640
  showTooltip(e) {
4641
+ if (!this.tooltip) {
4642
+ return;
4643
+ }
4475
4644
  this.tooltip.classList.add('svgMap-active');
4645
+
4646
+ if (
4647
+ this.options.showTooltips &&
4648
+ this.options.tooltipTrigger === 'click' &&
4649
+ e.pointerType === 'mouse'
4650
+ ) {
4651
+ // don't register event listener in the same frame
4652
+ // to prevent it from being triggered immediately
4653
+ requestAnimationFrame(() => {
4654
+ document.addEventListener(
4655
+ 'pointerdown',
4656
+ this._clickTooltipOutsideHandler,
4657
+ { once: true, passive: true }
4658
+ );
4659
+ });
4660
+ }
4661
+
4476
4662
  this.moveTooltip(e);
4477
4663
  }
4478
4664
 
4479
4665
  // Hide the tooltip
4480
4666
 
4481
4667
  hideTooltip() {
4668
+ if (!this.tooltip) {
4669
+ return;
4670
+ }
4671
+
4482
4672
  this.tooltip.classList.remove('svgMap-active');
4673
+ document.removeEventListener(
4674
+ 'pointerdown',
4675
+ this._clickTooltipOutsideHandler
4676
+ );
4483
4677
  }
4484
4678
 
4485
4679
  // Move the tooltip
4486
4680
 
4487
4681
  moveTooltip(e) {
4682
+ if (!this.tooltip) {
4683
+ return;
4684
+ }
4488
4685
  var x = e.pageX || (e.touches && e.touches[0] ? e.touches[0].pageX : null);
4489
4686
  var y = e.pageY || (e.touches && e.touches[0] ? e.touches[0].pageY : null);
4490
4687