8.8. Работа с javascript/jQuery в Drupal 8. Что такое behaviors?

Для начала давайте вернемся к тому как подключать кастомные javascript файлы к нашей теме. В файле .libraries.yml нужно подключить js:

global-styling:
  version: 1.x
  css:
    theme:
      css/style.css: {}
      css/print.css: { media: print }
  js:
    js/custom.js: {}
  dependencies:
    - core/jquery
    - core/jquery.once

Важно соблюдать отступы, чтобы он был в два пробела. Итак, мы подключили файл js/custom.js. Но этого не достаточно чтобы у нас работал jQuery. Дело в том что ядро друпала не требует jQuery и jQuery не подключается. Его нужно подключать отдельно:

  dependencies:
    - core/jquery

Также мы будем использовать jQuery.once(), это отдельный плагин для jQuery, для того чтобы навешивать события и методы на селектор только однажды.

  dependencies:
    - core/jquery
    - core/jquery.once

Дело в том, что мы будем писать код javascript, который будет вызывать друпалом несколько раз по разным событиям. Поэтому нам будет нужен этот метод .once(). 

Теперь давайте добавим немного кода в файл custom.js:

(function ($) {
  Drupal.behaviors.myModuleBehavior = {
    attach: function (context, settings) {
      $(context).find('.click-me').once('myCustomBehavior').click(function () {
        alert('Hello, World!');
      });
    }
  };
})(jQuery);

Давайте разберем по порядку, что это все значит.

(function ($) {

})(jQuery);

Мы оборачиваем код jQuery в такую конструкцию, потому что jQuery в друпале запускается в режиме .noConflict(). Это нужно для того чтобы использовать знак доллара $, и это не конфликтовало с другими javascript фреймворками Prototype, MooTools. Вряд ли вам придется встретиться с этими фреймворками, jQuery плотно занял лидирующие позиции. Но в эту конструкцию вам придется оборачивать весь jQuery-код.

(function ($) {
  Drupal.behaviors.myModuleBehavior = {
    attach: function (context, settings) {

    }
  };
})(jQuery);

Вот мы и подошли к behavior'ам. Если вы пишите jQuery код в друпале, вам нужно его во-первых обернуть в function($), а во-вторых в behavior. Имя behavior'а должно быть уникальным, у нас в примере myModuleBehavior, но вам нужно для каждого behavior писать свое имя:

(function ($) {
  Drupal.behaviors.myModuleBehavior = {
    attach: function (context, settings) {

    }
  };

  Drupal.behaviors.productPageBehavior = {
    attach: function (context, settings) {

    }
  };
})(jQuery);


Используйте camelCase для наименования behavior'ов. Behavior вызывается при загрузке страницы, на каждый AJAX-запрос. Таким образом, когда на сайт подгружается новый контент и встраивается в структуру существующего сайта, то вызывается код из behavior, каждый раз. Это значительно отличается от конструкции:

$(document).ready(function () {
  // Do some fancy stuff.
  // Не используйте такой код в Drupal 8 (да и в Drupal 7 тоже).
});

которая вызывается только один раз при загрузке странице. 

Если вы начали использовать behavior'ы и заметили, что у вас происходят странные события с сайтом, например через jQuery блок добавляется несколько раз:

(function ($) {
  Drupal.behaviors.myModuleBehavior = {
    attach: function (context, settings) {
      // Behavior вызывается несколько раз на странице, не забывайте использовать функцию .once().
      $('.inner').append('<p>Test</p>');
    }
  };
})(jQuery);

 При каждом ajax-запросе у вас будет добавлять еще один параграф Test. Поэтому нужно добавить функцию .once():

(function ($) {
  Drupal.behaviors.myModuleBehavior = {
    attach: function (context, settings) {
      // Behavior вызывается несколько раз на странице, не забывайте использовать функцию .once().
      $('.inner').once('add-paragraph').append('<p>Test</p>');
    }
  };
})(jQuery);

 Еще одна фича behavior это переменная context. Каждый раз когда на сайт добавляется новый контент при загрузке страницы или через ajax, то весь новый контент находится в переменной context. Таким образом нам не нужно проходить все DOM дерево, после каждого ajax запроса, чтобы навесить событие на селектор. Достаточно пройтись только по context'у:

(function ($) {
  Drupal.behaviors.myModuleBehavior = {
    attach: function (context, settings) {
      // Behavior вызывается несколько раз на странице, не забывайте использовать функцию .once().
      $(context).find('.inner').once('add-paragraph').append('<p>Test</p>');
    }
  };
})(jQuery);

Теперь добавление параграфа написано в правильном друпал стиле. Конечно, вы можете использовать старую запись с document.ready(), но тогда это будет работать только один раз и медленнее чем через behavior'ы.

Если у вас возникнут вопросы по jQuery/javascript или предложения по дополнительным темам пишите в комментариях. 

Комментарии

Добавить комментарий

Войти, используя Loginza Google Account Yandex Mail.ru Vkontakte Facebook

Plain text

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и параграфы переносятся автоматически.

(function ($) {

(function ($) {
  Drupal.behaviors.myModuleBehavior = {
    attach: function (context, settings) {
      $(context).find('.click-me').once('myCustomBehavior').click(function () {
        test();
      });
    }
  };

  function test() {
    alert('test');
  }
})(jQuery);

Здравствуйте! Подскажите, я

Здравствуйте! Подскажите, я сделал вывод вьюсом блока новостей, с постраничным навигатором по 2 новости, навигатор через аякс.
В одной теме все работает как надо, а включаю другую постраничный навигатор через аякс не работает, просто перегружает страницу. все это через colorbox работает, в обоих темах все js необходимые подключены.
Что еще можно проверить?

Здраствуйте, нужно в первую

Здраствуйте, нужно в первую очередь посмотреть  console DOM-инспектора, если там ошибка, то ajax не будет срабатывать. Возможно также файл не подключается какой-нибудь javascript. Ajax может не работать если есть второй Views на этой же странице и там не используется ajax. Если вы открываете страницу через colorbox node в попапе, где уже есть страница с пагинатором, то это тоже может плохо работать, потому что для работы ajax нужна библиотека drupal. 

$form['#attached']['library'][] = 'core/drupal.dialog.ajax';

Можно подключить эту библиотеку глобально для темы в файле THEMENAME.libraies.info:

  dependencies:
    - core/jquery
    - core/jquery.once
    - core/drupal.dialog.ajax

И можно использовать в 8ом друпале вместо colorbox обычную ссылку, просто добавить к ней CSS класс .use-ajax и параметр для открывания в попапе data-dialog-type="modal":

<a href="/form/conact-us" class="use-ajax button" data-dialog-type="modal">Открыть попап</a>

http://www.mediacurrent.com/blog/loading-and-rendering-modal-forms-drupal-8 - здесь можно пример посмотреть формы с попапом.

Создал файл Customjs.js

Создал файл Customjs.js добавил туда код, прописал его  в mytheme.libraries.yml. Верхний фрагмент кода должен темизировать чекбоксы, проблема в том, что в модальном окне он выполняется несколько раз, а на странице модуля contact не хочет работать.

(function($){
 Drupal.behaviors.my_behavior = {
  attach: function(context, settings) {
jQuery(document).ready(function(){

/*************** Checkbox script ***************/
jQuery(document).bind('ready ajaxComplete', function() {
var inputs = document.getElementsByTagName('input');
for (a = 0; a < inputs.length; a++) {
if (inputs[a].type == "checkbox") {
var id = inputs[a].getAttribute("id");
if (id==null){
id=  "checkbox" +a;
}
inputs[a].setAttribute("id",id);
var container = document.createElement('div');
container.setAttribute("class", "bc_checkbox");
var label = document.createElement('label');
label.setAttribute("for", id);
jQuery(inputs[a]).wrap(container).after(label);
}
}
});

/*************** Radiobutton script ***************/
jQuery(document).bind('ready ajaxComplete', function() {
var inputs = document.getElementsByTagName('input');
for (a = 0; a < inputs.length; a++) {
if (inputs[a].type == "radio") {
var id = inputs[a].getAttribute("id");
if (id==null){
 id=  "radio" +a;
}
inputs[a].setAttribute("id",id);
var container = document.createElement('div');
container.setAttribute("class", "bc_radio");
var label = document.createElement('label');
label.setAttribute("for", id);
jQuery(inputs[a]).wrap(container).after(label);
}
}
});

/*************** Staticfooter script ***************/
var window_height =  Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
var body_height = jQuery(document.body).height();
var content = jQuery("#bc_content_and_sidebar_container");
if(body_height < window_height){
differ = (window_height - body_height);
content_height = content.height() + differ;
jQuery("#bc_content_and_sidebar_container").css("min-height", content_height+"px");
}

/* Slideshow Function Call */

if(jQuery('#bc_slideshow_inner').length){
jQuery('#bc_slideshow_inner').TTSlider({
slideShowSpeed:2000, begintime:1000,cssPrefix: 'bc_'
});
}

/*************** Hamburgermenu slideleft script ***************/
jQuery('#nav-expander').on('click',function(e){
e.preventDefault();
jQuery('body').toggleClass('nav-expanded');
});

/*************** Menu click script ***************/
jQuery('ul.bc_menu_items.nav li [data-toggle=dropdown]').on('click', function() {
var window_width =  Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
if(window_width > 1025 && jQuery(this).attr('href')){
window.location.href = jQuery(this).attr('href'); 
}
else{
if(jQuery(this).parent().hasClass('open')){
location.assign(jQuery(this).attr('href'));
}
}
});

/*************** Sidebarmenu click script ***************/
jQuery('ul.bc_vmenu_items.nav li [data-toggle=dropdown]').on('click', function() {
var window_width =  Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
if(window_width > 1025 && jQuery(this).attr('href')){
window.location.href = jQuery(this).attr('href'); 
}
else{
if(jQuery(this).parent().hasClass('open')){
location.assign(jQuery(this).attr('href'));
}
}
});

/*************** Tab menu click script ***************/
jQuery('.bc_menu_items ul.dropdown-menu [data-toggle=dropdown]').on('click', function(event) { 
var window_width =  Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
if(window_width < 1025){
event.preventDefault();
event.stopPropagation();
jQuery(this).parent().siblings().removeClass('open');
jQuery(this).parent().toggleClass(function() {
if (jQuery(this).is(".open") ) {
window.location.href = jQuery(this).children("[data-toggle=dropdown]").attr('href'); 
return "";
} else {
return "open";
}
});
}
});

/*************** Tab-Sidebarmenu script ***************/
jQuery('.bc_vmenu_items ul [data-toggle=dropdown]').on('click', function(event) { 
var window_width =  Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
if(window_width < 1025){
event.preventDefault();
event.stopPropagation();
jQuery(this).parent().siblings().removeClass('open');
jQuery(this).parent().toggleClass(function() {
if (jQuery(this).is(".open") ) {
window.location.href = jQuery(this).children("[data-toggle=dropdown]").attr('href'); 
return "";
} else {
return "open";
}
});
}
});

/*************** Sticky menu script ***************/
var menuheight = jQuery('nav.navbar').height();
var menutop = jQuery('#bc_menu').offset().top;
 jQuery(document).scroll(function () {
var scrollTop = jQuery(this).scrollTop();
var menuOffset = menutop - menuheight;
if(menuOffset < 0)
{
menuOffset = 0;
}
if (scrollTop > menutop) 
{
jQuery('#bc_menu').addClass('navbar-fixed-top');
}
else if (scrollTop <= menuOffset)
{
jQuery('#bc_menu').removeClass('navbar-fixed-top');
}
});
WebFontConfig = {
google: { families: [ 'Open+Sans:700','Open+Sans','Varela+Round','Varela+Round:700'] }
};
(function() {
var wf = document.createElement('script');
wf.src = ('https:' == document.location.protocol ? 'https' : 'http') + '://ajax.googleapis.com/ajax/libs/webfont/1.0.31/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})();
});   
  }}
})(jQuery);