Доброго времени суток, уважаемое сообщество...
Сей пост про использование стандартной библиотеки всплывающих окон PopupWindow.
Для начала тем, кто еще не знаком, стоит познакомиться с официальной и с :
Итак ...
Была у меня задача - сделать аяксовую сменую 3-х отображений списка товаров инфоблока. Об этом писать не буду. Тут достаточно изучить . Это успешно было реализовано. Все обрадовались, попили пива ... А дальше начали детально все тестировать и обнаружили, что стандартная всплывашка, которая появилась в eshop и реализованная через JCCatalogSection после аякс-показа элемента - просто не отрабатывает. Потратил часа 4 - так и не разобрался, что же нужно сделать, чтобы работала ... Посему, решил сделать свою.
Создал отдельный js-файл, подключил его сразу в хедере (знаю, что можно было подключить и только на тех страницах, где это нужно, но не стал уже заморачиваться.... хотя стоило бы:) ), а также подключил в хедере popup-библиотеку (jquery был уже подключен. Если у вас не подключен - его тоже нужно):
CJSCore::Init(array("popup"));
Представляю вашему вниманию текст данного файла:
BX.ready(function(){
var Confirmer = new BX.PopupWindow("add_basket-confirm", null, {
content: '<div id="mainshadow"></div>'+'<h3>Товар успешно добавлен в корзину</h3>',
titleBar: {content: BX.create("span", {html: 'Товар добавлен в корзину', 'props': {'className': 'access-title-bar'}})},
zIndex: 0,
autoHide : true,
offsetTop : 1,
offsetLeft : 0,
lightShadow : true,
closeIcon : true,
closeByEsc : true,
draggable: {restrict: false},
overlay: {backgroundColor: 'black', opacity: '80' }, /* затемнение фона */
buttons: [
new BX.PopupWindowButton({
text: "Перейти в корзину",
className: "popup-window-button-accept",
events: {click: function(){
location.href="/personal/cart/";
}}
}),
new BX.PopupWindowButton({
text: "Продолжить покупки",
className: "webform-button-link-cancel",
events: {click: function(){
this.popupWindow.close(); // закрытие окна
}}
})
]
});
function PAIAddToBasket(PRODUCT_ID,QntTy){
$.post('/basketchange.php',
{
itemID: PRODUCT_ID,
quantity: QntTy
},
function(data){
$('div#basket-line').html(data);
Confirmer.show();
}
);
}
$('div.bx_content_section').on('click','.buy_btn',function(){
var PRODUCT_ID = $(this).attr('product_id');
var QntTy = 1;
PAIAddToBasket(PRODUCT_ID,QntTy);
return false;
});
});
и в подвале помещаем див:
<div id="add_basket-confirm"></div>
ну и последний штрих: во всех видах отображения у ссылок, отвечающих за добавление товара в корзину, добавляем класс "buy_btn". Примерно вот так:
<a class="buy_btn" href="#" product_id="<?=$arItem['ID']?>" > В корзину </a>
обычно в атрибут "product_id" помещаю ID товара, который нужно добавить в корзину. Не совсем корректно, но ничего умнее пока не придумал:)
Суть скрипта: вешаем на ссылку с классом buy_btn обработчик, который получает ID товара и количество единиц, которое нужно добавить (в данном случае - всегда 1
)и это все передаем в функцию, которая делает аякс- запрос к файлу, добавляющему товар в корзину (этот файл возвращает обратно компонент "Ссылка на корзину", который также обновляем при добавлении товара в корзину) и вызывает всплывашку.
P.S. Если не хотите портить дефолтные стили у шаблонов от eshop - просто удалите атрибут ID у ссылок добавления в корзину:)
UPDATE 2014-09-22
Увидел еще одну штуку: если нужно какое-то событие после показа popup-окна, то добавляется еще один параметр, описывающий события popup:
events: {
onPopupClose: function () {
console.debug('onPopupClose event');
},
onAfterPopupShow: function () {
BX.ajax.post(
' echo $this->GetFolder(); ?>/popup.php',
{
lang: BX.message('LANGUAGE_ID'),
site_id: BX.message('SITE_ID') || '',
arParams: curSetParams
},
BX.delegate(function (result) {
this.setContent(result);
BX("CatalogSetConstructor_" + element_id).style.left = (window.innerWidth - BX("CatalogSetConstructor_" + element_id).offsetWidth) / 2 + "px";
var popupTop = document.body.scrollTop + (window.innerHeight - BX("CatalogSetConstructor_" + element_id).offsetHeight) / 2;
//BX("CatalogSetConstructor_" + element_id).style.top = popupTop > 0 ? popupTop + "px" : 0;
},
this)
);
}
}
Это увидел в компоненте catalog.set.constructor. Оставил так, как было в исходнике. Суть: сначала в попапе показывается прелоадер, а при открытии из файла popup.php тянется содержимое попапа ... Но, конечно же, можно сделать еще кучу других очень полезных фишек, используя данный метод ...
UPDATE 2017-09-26
Если вам нужно вывести во всплывашке что-то, загруженное по аяксу - есть 2 варианта рещения:
- Если текст должне подтягиваться каждый раз перед показом - значит подгружать текст нужно перед показом его пользователю. Т.е. функция установки данных должна вызываться перед показом popup-а.
- Содержимое попапа должно получаться по аяксу, но один раз на страницу - тогда получение данных по аяксу лучшу сделать перед определением самого popup-а.
Для примера рассмотрю второй вариант. Итак, в html-структуре создаем контейнер, из которого всплывашка будет получать данные:
<div id="popup_container"></div>
И начало для popup тогда примет вид:
BX.ready(function(){
BX.ajax.insertToNode('/ajax_path.php', BX('popup_container'));
var Confirmer = new BX.PopupWindow("add_basket-confirm", null, {
content: BX('popup_container'),
.....
Благодаря тому, что контент залили в контейнер мы до определения всплывашки - центрирование произойдет автоматически. Если же нужно контент подтягивать перед самим показом всплывашки - значит для контейнера, в который заливаем данные по аяксу, нужно обязательно задать в стилях ширину, т.к. центр рассчитывается библиотекой на основании ширины этого контейнера.
UPDATE 2020-11-29
Пример использования библиотеки для создания формы обратной связи с простейшей превалидацией полей:
BX.ready(function(){
var formReviewErrors = false;
var popupFields = BX.create({
tag: 'form',
props: {
className: 'formFieldsContainer',
action: '/ajax/',
id: 'reviewContainerForm',
method:'post'
},
children: [
BX.create({
tag: 'input',
props: {
name: 'sessid',
type: 'hidden',
value: BX.bitrix_sessid()
}
}),
BX.create({
tag: 'input',
props: {
name: 'action',
type: 'hidden',
value: 'addReview'
}
}),
BX.create({
tag:'div',
props: {
className: "form-group"
},
children: [
BX.create({
tag: 'label',
props: {for: 'reviewName'},
text: 'Ваше имя'
}),
BX.create({
tag: 'input',
props: {
id: 'reviewName',
type:'text',
className:'form-control',
placeholder:'Ваше имя',
name:'reviewName'
},
events: {
change: function (){
if(
this.value.length<=0 ||
!/[а-яА-ЯЁё]/.test(this.value)
){
BX.adjust(BX(this),{
props:{className: "form-control invalid"}
});
return false;
} else {
BX.adjust(BX(this),{
props:{className: "form-control"}
});
return true;
}
},
input: function (){
if(this.value.length<=0 ||
!/[а-яА-ЯЁё]/.test(this.value)
){
BX.adjust(BX(this),{
props:{className: "form-control invalid"}
});
return false;
} else {
BX.adjust(BX(this),{
props:{className: "form-control"}
});
return true;
}
}
}
}),
]
}),
BX.create({
tag:'div',
props: {
className: "form-group"
},
children: [
BX.create({
tag: 'label',
props: {for: 'reviewText'},
text: 'Ваш отзыв'
}),
BX.create({
tag: 'textarea',
props: {
id: 'reviewText',
name: 'reviewText',
rows:3,
className:'form-control'},
events: {
change: function (){
if(this.value.length<=0 ||
!/[а-яА-ЯЁё]/.test(this.value)){
BX.adjust(BX(this),{
props:{className: "form-control invalid"}
});
return false;
} else {
BX.adjust(BX(this),{
props:{className: "form-control"}
});
return true;
}
},
input: function (){
if(this.value.length<=0 ||
!/[а-яА-ЯЁё]/.test(this.value)){
BX.adjust(BX(this),{
props:{className: "form-control invalid"}
});
return false;
} else {
BX.adjust(BX(this),{
props:{className: "form-control"}
});
return true;
}
}
}
}),
]
}),
]
});
var addReviewForm = new BX.PopupWindow("addReviewForm", null, {
content: popupFields,
titleBar: {content: BX.create("div", {
html: 'Оставить отзыв о работе',
props: {'className': 'access-title-bar'}
})},
zIndex: 0,
autoHide : true,
offsetTop : 1,
offsetLeft : 0,
lightShadow : true,
closeIcon : true,
closeByEsc : true,
draggable: {restrict: false},
overlay: {backgroundColor: 'black', opacity: '80' },
buttons: [
new BX.PopupWindowButton({
id: 'reviewSuccessBtn',
text: "Оставить отзыв",
className: "btn btn-success",
events: {
click: function (){
BX.fireEvent(BX('reviewName'),'change');
BX.fireEvent(BX('reviewText'),'change');
if(BX.findChildren(
BX('reviewContainerForm'),{class:'invalid'},true
).length>0){
return false;
} else {
BX.ajax.submitAjax(BX('reviewContainerForm'),
{
method: 'POST',
dataType: 'json',
onsuccess: function(data){
if(data.SUCCESS_ID!==undefined){
BX.adjust(BX('reviewContainerForm'),{
html:'Ваш отзыв будет опубликован после проверки модератором!'
});
BX.cleanNode(BX('reviewSuccessBtn'),true);
} else if(data.ERROR!==undefined){
alert(data.ERROR); // TODO: реализовать вывод тут же в форме
}
},
onfailure: function(data){
console.debug('error');
console.debug(data);
}
});
}
}
}
})
]
});
let ReviewsBtnContainer = BX('addRewiewBtnContainer');
let Btn = BX.create({
tag: "button",
props: {className: "btn btn-info btn-large"},
text: 'Оставить отзыв',
events: {
click: function () {
addReviewForm.show();
}
}
});
BX.append(Btn,ReviewsBtnContainer);
});
Если в процессе работы со всплывашкой ее ширины меняется - сталкиваемсяс проблемой отсутствия центрированности окна. Чтобы отцентрировать окно после изменения его ширины, необходимо вызвать функцию центрирования:
Confirmer.adjustPosition();
