<script src="https://api-maps.yandex.ru/2.1.73/?lang=ru_RU"></script>
<link rel="stylesheet" href="https://yandex.st/bootstrap/2.3.2/css/bootstrap.min.css">
<div id="map"></div>
<button id="set-balloon-header" class="btn">Задать заголовок балуна</button>
<button id="set-balloon-content" class="btn">Задать содержимое балуна</button>
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
#map {
height: 85%;
}
.popover {
display: block;
}
.popover .close {
position: absolute;
right: 5px;
top: 1px;
}
.btn {
margin-top: 10px;
}
ymaps.ready(function () {
// Создание экземпляра карты и его привязка к созданному контейнеру.
var myMap = new ymaps.Map('map', {
center: [55.751574, 37.573856],
zoom: 9,
behaviors: ['default', 'scrollZoom']
}, {
searchControlProvider: 'yandex#search'
}),
// Создание макета балуна на основе Twitter Bootstrap.
MyBalloonLayout = ymaps.templateLayoutFactory.createClass(
'<div class="popover top">' +
'<a class="close" href="#">×</a>' +
'<div class="arrow"></div>' +
'<div class="popover-inner">' +
'$[[options.contentLayout observeSize minWidth=235 maxWidth=235 maxHeight=350]]' +
'</div>' +
'</div>', {
/**
* Строит экземпляр макета на основе шаблона и добавляет его в родительский HTML-элемент.
* @see https://api.yandex.ru/maps/doc/jsapi/2.1/ref/reference/layout.templateBased.Base.xml#build
* @function
* @name build
*/
build: function () {
this.constructor.superclass.build.call(this);
this._element = this.getParentElement().querySelector('.popover');
this._onCloseClick = this.onCloseClick.bind(this);
this.applyElementOffset();
this._element.querySelector('.close').addEventListener('click', this._onCloseClick);
},
/**
* Удаляет содержимое макета из DOM.
* @see https://api.yandex.ru/maps/doc/jsapi/2.1/ref/reference/layout.templateBased.Base.xml#clear
* @function
* @name clear
*/
clear: function () {
this._element.querySelector('.close').removeEventListener('click', this._onClickClick);
this.constructor.superclass.clear.call(this);
},
/**
* Метод будет вызван системой шаблонов АПИ при изменении размеров вложенного макета.
* @see https://api.yandex.ru/maps/doc/jsapi/2.1/ref/reference/IBalloonLayout.xml#event-userclose
* @function
* @name onSublayoutSizeChange
*/
onSublayoutSizeChange: function () {
MyBalloonLayout.superclass.onSublayoutSizeChange.apply(this, arguments);
if (!this._isElement(this._element)) {
return;
}
this.applyElementOffset();
this.events.fire('shapechange');
},
/**
* Сдвигаем балун, чтобы "хвостик" указывал на точку привязки.
* @see https://api.yandex.ru/maps/doc/jsapi/2.1/ref/reference/IBalloonLayout.xml#event-userclose
* @function
* @name applyElementOffset
*/
applyElementOffset: function () {
Object.assign(this._element.style, {
left: -(this._element.offsetWidth / 2) + 'px',
top: -(this._element.offsetHeight + this._element.querySelector('.arrow').offsetHeight) + 'px'
});
},
/**
* Закрывает балун при клике на крестик, кидая событие "userclose" на макете.
* @see https://api.yandex.ru/maps/doc/jsapi/2.1/ref/reference/IBalloonLayout.xml#event-userclose
* @function
* @name onCloseClick
*/
onCloseClick: function (e) {
e.preventDefault();
this.events.fire('userclose');
},
/**
* Используется для автопозиционирования (balloonAutoPan).
* @see https://api.yandex.ru/maps/doc/jsapi/2.1/ref/reference/ILayout.xml#getClientBounds
* @function
* @name getClientBounds
* @returns {Number[][]} Координаты левого верхнего и правого нижнего углов шаблона относительно точки привязки.
*/
getShape: function () {
if (!this._isElement(this._element)) {
return MyBalloonLayout.superclass.getShape.call(this);
}
var style = getComputedStyle(this._element);
var position = {
left: parseFloat(style.left),
top: parseFloat(style.top),
};
return new ymaps.shape.Rectangle(new ymaps.geometry.pixel.Rectangle([
[position.left, position.top], [
position.left + this._element.offsetWidth,
position.top + this._element.offsetHeight + this._element.querySelector('.arrow').offsetHeight
]
]));
},
/**
* Проверяем наличие элемента (в ИЕ и Опере его еще может не быть).
* @function
* @private
* @name _isElement
* @param {jQuery} [element] Элемент.
* @returns {Boolean} Флаг наличия.
*/
_isElement: function (element) {
return element && element.querySelector('.arrow');
}
}),
// Создание вложенного макета содержимого балуна.
MyBalloonContentLayout = ymaps.templateLayoutFactory.createClass(
'<h3 class="popover-title">$[properties.balloonHeader]</h3>' +
'<div class="popover-content">$[properties.balloonContent]</div>'
),
// Создание метки с пользовательским макетом балуна.
myPlacemark = window.myPlacemark = new ymaps.Placemark(myMap.getCenter(), {
balloonHeader: 'Заголовок балуна',
balloonContent: 'Контент балуна'
}, {
balloonShadow: false,
balloonLayout: MyBalloonLayout,
balloonContentLayout: MyBalloonContentLayout,
balloonPanelMaxMapArea: 0
// Не скрываем иконку при открытом балуне.
// hideIconOnBalloonOpen: false,
// И дополнительно смещаем балун, для открытия над иконкой.
// balloonOffset: [3, -40]
});
myMap.geoObjects.add(myPlacemark);
});
document.querySelector('#set-balloon-header').addEventListener('click', function() {
window.myPlacemark.properties.set(
'balloonHeader',
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
+ 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'
);
});
document.querySelector('#set-balloon-content').addEventListener('click', function() {
window.myPlacemark.properties.set(
'balloonContent',
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
+ 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'
);
});