
В своем прошлом топике, я описал базовые принципы работы с фреймворком backbone.js, теперь предлагаю перейти к практике и сделать что-нибудь полезное.
Задача
Предположим, что на нашем сайте часто используются разного вида попапы. Все они обладают схожими чертами, их можно открывать в большом количестве, перетаскивать, закрывать. Кроме того различаются активные и неактивные попапы, причем активный расположен поверх остальных и не затенен (хм… я бы сказал, что это уже больше напоминает window-manager).
Вобщем как-то так:

В соответствии с заветами ООП, попробуем разработать класс для попапа, от которого мы сможем наследоваться и создавать попапы на свой вкус с общим для всех поведением.
Скачиваем backbone.js, jquery, jquery ui, underscore.js и
index.html
Для начала создадим индексный файл с подключенными библиотеками, файлами нашего (еще не написанного) приложения и шаблонами.
<!doctype html>
<html>
<head>
<title>Popup demo</title>
<link rel="stylesheet" href="css/main.css" />
<!-- libs -->
<script type="text/javascript" src="js/lib/jquery.js"></script>
<script type="text/javascript" src="js/lib/jquery-ui.js"></script>
<script type="text/javascript" src="js/lib/underscore.js"></script>
<script type="text/javascript" src="js/lib/backbone.js"></script>
<!-- app -->
<script type="text/javascript" src="js/app/app.js"></script>
<script type="text/javascript" src="js/app/views/PopupView.js"></script>
<script type="text/javascript" src="js/app/views/ChildPopupView.js"></script>
<!-- templates -->
<script id="popup-template" type="text/template">
<div class="title">
<h1><%= title %></h1>
</div>
<div class="content">
<%= content %>
</div>
<div class="popup-close">
</div>
</script>
<script id="child-popup-template" type="text/template">
<div class="title">
<h1><%= title %></h1>
<h3>i am a child</h3>
</div>
<div class="content">
<%= content %>
</div>
<div class="popup-close">
</div>
</script>
</head>
<body>
<button id="popup-button">popup</button>
<button id="child-popup-button">child popup</button>
</body>
</html>
В «body» у нас только 2 кнопки с помощью которых мы будем создавать попапы.
app.js
В этом файлике на клики по кнопкам навешивается код, создающий попапы, и добавляющий их в наш DOM.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
$(function () {
$('#popup-button').click(function () {
var popup = new PopupView();
$('body').append(popup.render().el);
});
$('#child-popup-button').click(function () {
var popup = new ChildPopupView();
$('body').append(popup.render().el);
});
});
Так же я сюда запихнул функцию для получения случайного целого числа, она нам потом пригодится.
И теперь самое интересное!
PopupView.js
var PopupView = Backbone.View.extend({
className: 'popup',
events: {
'click .popup-close': 'close',
'mousedown': 'raise'
},
initialize: function () {
this.template = $('#popup-template').html();
this.width = '400px';
this.height = '350px';
this.top = getRandomInt(100, 400) + 'px';
this.left = getRandomInt(200, 500) + 'px';
this.context = {
title: 'Default Title',
content: 'Lorem ipsum dolor sit amet.'
}
$(this.el).css({
'width': this.width,
'height': this.height,
'top': this.top,
'left': this.left,
'position': 'absolute'
});
$(this.el).draggable();
},
/**
* Рендерит попап и располагает его поверх остальных.
*/
render: function () {
$(this.el).html(_.template(this.template, this.context));
// Делаем декущий попап активным
$('.popup-active').removeClass('popup-active');
$(this.el).addClass('popup-active');
// Начальный z-index, минимальное значение которое может получить попап
var max_z = 100;
$('.popup').each(function () {
var curr_z = parseInt($(this).css('z-index'));
if (curr_z > max_z) {
max_z = curr_z;
}
});
$(this.el).css({ 'z-index': max_z + 10 });
return this;
},
/**
* Поднимает попап наверх и делает его активным
* по нажатию кнопки мыши.
*/
raise: function (e) {
if (!$(this.el).hasClass('popup-active')) {
var max_z = 0;
$('.popup-active').removeClass('popup-active');
$('.popup').each(function () {
var curr_z = parseInt($(this).css('z-index'));
if (curr_z > max_z) {
max_z = curr_z;
}
});
$(this.el).css({ 'z-index': max_z + 10 });
$(this.el).addClass('popup-active')
}
},
/**
* Удаляет попап из DOM.
*/
close: function () {
$(this.el).remove();
var max_z = 0;
var top = null;
// Выбираем самый верхний их оставшихся попапов
$('.popup').each(function () {
var curr_z = parseInt($(this).css('z-index'));
if (curr_z > max_z) {
max_z = curr_z;
top = this;
}
});
// Делаем его активным
if (top) {
$(top).addClass('popup-active');
}
}
});
Здесь у нас описан попап, который станет родителем для всех остальных попапов, в нем реализован весь типичный функционал.
Теперь, для того чтобы создать новый попап, реализующий какое-то новое поведение, или использующий другой шаблон нам потребуется всего несколько строк кода.
ChildPopupView.js
var ChildPopupView = PopupView.extend({
initialize: function () {
ChildPopupView.__super__.initialize.call(this);
this.template = $('#child-popup-template').html();
this.context = {
title: 'Child Title',
content: 'Lorem ipsum dolor sit amet.'
}
}
});
В этом потомке мы подключили другой шаблон и поменяли заголовок попапа. Вот так вот все просто!
Код демки