Custom balloon layout with autopositioning
To create your own balloon "from scratch", you can use the templateLayoutFactory factory. However, the balloon class must implement the IBalloon interface.
In order for the balloon to support autopositioning, meaning it automatically adjusts to the size of the content, you need to correctly state its current size. To do this, implement the getShape() method.
When you change the content of a balloon in the onSublayoutSizeChange() method, override the offset option and throw the shapechange event.
index.html
balloon_autopan.js
<!DOCTYPE html>
<html>
<head>
<title>Examples. Custom balloon layout with autopositioning.</title>
<meta
http-equiv="Content-Type"
content="text/html; charset=UTF-8"
/>
<link
href="https://yandex.st/bootstrap/2.3.2/css/bootstrap.min.css"
rel="stylesheet"
/>
<script
src="https://yandex.st/jquery/2.2.3/jquery.min.js"
type="text/javascript"
></script>
<!--
Set your own API-key. Testing key is not valid for other web-sites and services.
Get your API-key on the Developer Dashboard: https://developer.tech.yandex.ru/keys/
-->
<script
src="https://api-maps.yandex.ru/2.1/?lang=en_US&apikey=<your API-key>"
type="text/javascript"
></script>
<script src="balloon_autopan.js" type="text/javascript"></script>
<style>
#map {
width: 400px;
height: 260px;
}
.popover {
display: block;
}
.popover .close {
position: absolute;
right: 5px;
top: 1px;
}
.btn {
margin-top: 10px;
}
</style>
</head>
<body>
<div id="map"></div>
<button id="set-balloon-header" class="btn">
Set the title of a balloon
</button>
<button id="set-balloon-content" class="btn">
Set the contents of a balloon
</button>
</body>
</html>
ymaps.ready(function () {
// Creating an instance of the map and binding it to the created container.
var myMap = new ymaps.Map(
"map",
{
center: [55.751574, 37.573856],
zoom: 9,
behaviors: ["default", "scrollZoom"],
},
{
searchControlProvider: "yandex#search",
}
),
// Creating a balloon layout based on 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>",
{
/**
* Builds an instance of a layout based on a template and adds it to the parent HTML element.
* @see https://api.yandex.com/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 = $(".popover", this.getParentElement());
this.applyElementOffset();
this._$element
.find(".close")
.on("click", $.proxy(this.onCloseClick, this));
},
/**
* Removes the layout contents from DOM.
* @see https://api.yandex.com/maps/doc/jsapi/2.1/ref/reference/layout.templateBased.Base.xml#clear
* @function
* @name clear
*/
clear: function () {
this._$element.find(".close").off("click");
this.constructor.superclass.clear.call(this);
},
/**
* The method will be invoked by the API's template system when resizing the nested layout.
* @see https://api.yandex.com/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");
},
/**
* Moving the balloon so the "tail" points at the anchor point.
* @see https://api.yandex.com/maps/doc/jsapi/2.1/ref/reference/IBalloonLayout.xml#event-userclose
* @function
* @name applyElementOffset
*/
applyElementOffset: function () {
this._$element.css({
left: -(this._$element[0].offsetWidth / 2),
top: -(
this._$element[0].offsetHeight +
this._$element.find(".arrow")[0].offsetHeight
),
});
},
/**
* Closes the balloon when the "x" is clicked, throwing the "userclose" event on the layout.
* @see https://api.yandex.com/maps/doc/jsapi/2.1/ref/reference/IBalloonLayout.xml#event-userclose
* @function
* @name onCloseClick
*/
onCloseClick: function (e) {
e.preventDefault();
this.events.fire("userclose");
},
/**
* Used for autopositioning (balloonAutoPan).
* @see https://api.yandex.com/maps/doc/jsapi/2.1/ref/reference/ILayout.xml#getClientBounds
* @function
* @name getClientBounds
* @returns {Number[][]} The coordinates of the top left and bottom right corners of the template relative to the anchor point.
*/
getShape: function () {
if (!this._isElement(this._$element)) {
return MyBalloonLayout.superclass.getShape.call(
this
);
}
var position = this._$element.position();
return new ymaps.shape.Rectangle(
new ymaps.geometry.pixel.Rectangle([
[position.left, position.top],
[
position.left +
this._$element[0].offsetWidth,
position.top +
this._$element[0].offsetHeight +
this._$element.find(".arrow")[0]
.offsetHeight,
],
])
);
},
/**
* Checking the availability of the item (in IE and Opera it might not be there yet).
* @function
* @private
* @name _isElement
* @param {jQuery} [element] Element.
* @returns {Boolean} Availability flag.
*/
_isElement: function (element) {
return (
element && element[0] && element.find(".arrow")[0]
);
},
}
),
// Creating a nested layout for balloon content.
MyBalloonContentLayout = ymaps.templateLayoutFactory.createClass(
'<h3 class="popover-title">$[properties.balloonHeader]</h3>' +
'<div class="popover-content">$[properties.balloonContent]</div>'
),
// Creating a placemark with a custom balloon layout.
myPlacemark = (window.myPlacemark = new ymaps.Placemark(
myMap.getCenter(),
{
balloonHeader: "Balloon header",
balloonContent: "Balloon content",
},
{
balloonShadow: false,
balloonLayout: MyBalloonLayout,
balloonContentLayout: MyBalloonContentLayout,
balloonPanelMaxMapArea: 0,
/**
* Not hiding the icon when the balloon is open.
* hideIconOnBalloonOpen: false,
* And additionally moving the balloon to open over the icon.
* balloonOffset: [3, -40]
*/
}
));
myMap.geoObjects.add(myPlacemark);
});
$(function () {
$("#set-balloon-header").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."
);
});
$("#set-balloon-content").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."
);
});
});