Мой опыт работы с BX.PopupWindow

Продолжаю перенос всех своих наработок из своего блога на сайте разработчиков компании 1С-Битрикс. На этото раз пост про использование стандартной битриксовой библиотеки всплывающих окон PopupWindow

Доброго времени суток, уважаемое сообщество... 

Сей пост про использование стандартной библиотеки всплывающих окон 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(
            '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();
Количество показов: 46248
09.08.2014

Возврат к списку

Если вам была полезна статья можете отблагодарить автора:
Ethereum:

0x16Df809287333C49D3A237296C6248A6c08702Bc

Разработка сайта

Подайте заявку на разработку сайта на базе готового решения от компании 1С-Битрикс или одного из партнеров компании. Максимально подробно опишите, чему будет посвящен сайт, если это интернет-магазин - что он будет продавать, нужна ли мультиязычность, будут ли разные типы цен (розница, опт, крупный опт), будет ли интеграция с 1С, будет ли выгрузка товаров на различные торговые площадки...

Сопровождение сайта

Вы можете подать заявку на сопровождение вашего сайта на базе 1С-Битрикс. Сопровождение включает в себя: проверка актуальности обновлений сайта, проверка актуальности резервной копии, консультации по сайту. Опишите в заявке, какие еще объемы планируются на сопровождении и на какой срок вы планируете заключить договор на сопровождение - мы подберем подходящий вам бюджет на сопровождение

Работы по сайту

Вы можете подать заявку на выполнение определенного объема работ по сайту. Опишите в заявке объем работ. Это может быть разработка какого-то нового функционала, доработки по имеющемуся функционалу, доработки под требования сео-специалистов. На основании заявки вам будет сформирован бюджет работ, а также названы сроки на выполнение тех или иных работ.