JS Класс для работы с яндекс картами

Данная заготовка позволяет создавать на сайте множество карт яндекса, на них размещать метки, добавлять в метки контент, перемещаться к меткам по нажатию кнопок.

Нам понадобится установить в проект библиотеку ymaps:

npm i ymaps

Затем добавляем в проект js скрипт ymaps.js:

ymaps.js
import ymaps from 'ymaps';

/**
 * @typedef {Object} MapOptions
 * @property {number} zoom - Уровень масштабирования карты
 * @property {string} ico - URL иконки маркера
 * @property {number} iconWidth - Ширина иконки маркера
 * @property {number} iconHeight - Высота иконки маркера
 * @property {boolean} isScroll - Разрешен ли скролл для карты
 * @property {string} centerX - Координата X центра карты
 * @property {string} centerY - Координата Y центра карты
 */

/**
 * @typedef {Object} MapPoint
 * @property {string} x - Координата X точки на карте
 * @property {string} y - Координата Y точки на карте
 * @property {string} desc - Описание точки
 */

/**
 * @callback CallbackFn
 * @param {YMap} e - Экземпляр карты
 */

function isMobileDevice() {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}

export default class YMap {
    /**
   * @param {string} id - Идентификатор элемента карты
   * @param {CallbackFn} callbackFn - Функция обратного вызова
   */
    constructor(id, callbackFn) {
        this._id = id;
        this._clFn = callbackFn;
        this._$ = document.getElementById(id);
        this._options = Object.assign(this.loadOptionsDefault(), { ...this._$.dataset });
        this._defaultIcon = this.createIcon();
        this._listMarkers = {};
        this._markers = this._$ && Array.from(this._$.querySelectorAll('[data-x]'))
            .filter((point) => point.dataset.x && point.dataset.y)
            .map((point) => ({
                desc: point.innerHTML.trim() || '',
                x: point.dataset.x || '',
                y: point.dataset.y || '',
            })) || [];

        this.init().then(() => this._markers.forEach((point) => this.addMarker(point)));
    }

    /**
   * Инициализация карты
   * @returns {Promise<void>}
   */
    async init() {
        await ymaps
            .load('//api-maps.yandex.ru/2.1/?lang=ru_RU')
            .then((maps) => {
                this._maps = maps;
                this._map = new maps.Map(this._id, {
                    center: [this._options.centerX, this._options.centerY],
                    zoom: this._options.zoom,
                    controls: ['zoomControl', 'fullscreenControl'],
                });
            })
            .catch((error) => console.log('Failed to load Yandex Maps', error));

        this._map.behaviors.disable('scrollZoom'); // проверяем, надо ли отрубать скролл
        if (isMobileDevice()) {
            this._map.behaviors.disable('drag');
        }
        this._clFn(this);
    }

    /**
   * Загрузка настроек по умолчанию
   * @returns {MapOptions} Объект с настройками карты по умолчанию
   */
    loadOptionsDefault() {
        return {
            zoom: 16,
            ico: '/assets/img/point-map.svg',
            iconWidth: 30,
            iconHeight: 30,
            isScroll: false,
            centerX: '',
            centerY: '',
        };
    }

    /**
   * Создание иконки маркера
   * @param {string} [ico] - URL иконки (если передано)
   * @returns {Object} Настройки иконки
   */
    createIcon(ico) {
        return {
            url: ico || this._options.ico,
            size: [this._options.iconWidth, this._options.iconHeight],
            imgOffset: [-this._options.iconWidth / 2, -this._options.iconHeight],
            contentOffset: [0, 0],
        };
    }

    /**
   * Добавление маркера на карту
   * @param {MapPoint} point - Точка с координатами и описанием
   */
    addMarker(point) {
        const newMarker = new this._maps.Placemark([point.x, point.y], {
            hintContent: '',
            balloonContent: point.desc,
        }, {
            preset: 'islands#dotIcon',
            iconColor: '#FF8200',
        });

        this._listMarkers[`${point.x}-${point.y}`] = newMarker;
        this._map.geoObjects.add(newMarker);
    }
}

Импортируем в index.js класс YMap и добавим карту на нужные элементы. Дополнительно добавим функцию обработчик нажатия на кнопку, чтобы перемещаться к нужным точкам на карте:

main.js
import YMap from './ymap';

/**
 * Обработчик кликов по кнопкам, связанным с картой
 * @param {YMap} mapObj - Экземпляр карты
 */
function watcherBtn(mapObj) {
    const { _id, _map, _maps, _listMarkers, } = mapObj;
    const btnList = document.querySelectorAll(`[data-open-map=${_id}]`);

    btnList.forEach((btn) => {
        btn.addEventListener('click', () => {
            document.querySelector('body')?.scrollIntoView({
                behavior: 'smooth',
                block: 'start',
            });
        });

        _maps.domEvent.manager.group(btn)
            .add(['click'], async () => {
                const { x = '', y = '', zoom = '' } = btn.dataset;
                const marker = _listMarkers[`${x}-${y}`];
                const position = [parseFloat(x), parseFloat(y)];

                await _map.setCenter(position, zoom || 17, {
                    checkZoomRange: true,
                });
                marker?.balloon?.open();
            });
    });
}

document.addEventListener('DOMContentLoaded', () => {
    /** Инициализация карт * */
    Array.from(document.querySelectorAll('.map-box')).forEach((map) => {
        const id = map.getAttribute('id');
        if (!id) return;
        new YMap(id, watcherBtn);
    });
    /** --- Инициализация карт * */
});

Код разметки будет иметь следующий вид:

<div id="map-1" class="map-box" data-center-x="55.751999" data-center-y="37.617734" data-zoom="15">
    <div class="d-none" data-x="55.756" data-y="37.620">
      <p>Площадь <a href="#">революции</a></p>
    </div>
    <div class="d-none" data-x="55.751999" data-y="37.617734"></div>
</div>

<div>
    <button data-open-map="map-1" data-x="55.756" data-y="37.620" data-zoom="17">Смотреть на карте метку 1</button>
    <button data-open-map="map-1" data-x="55.751999" data-y="37.617734" data-zoom="17">Смотреть на карте метку 2</button>
</div>

Если в проекте используется bootstrap, то будет достаточно добавить только стили для класса обертки карты, иначе ещё нужно добавить стиль .d-none:

.map-box {
  width: 400px;
  height: 400px;
}

.d-none {
  display: none;
}

Готово!