Как получить текущий год на JavaScript?

const currentYear = new Date().getFullYear().toString();
  • new Date() — создаём объект который хранит в себе текущий момент времени
  • getFullYear() — выдёргиваем из объекта год указанной даты. Так как мы создавали экземпляр Date() без аргументов, то год будет соответствовать текущему моменту, а следовательно быть актуальным
  • toString() — возвращаем тип данных строку.

Ну вот и всё! Теперь в константе currentYear будет находится текущий год

пример

Как сделать маску (шаблон) в input при вводе номера телефона для WooCommerce?

Дано: Интернет-магазин на WordPress+WooCommerce

Задача: На странице оформления заказа в поле для ввода номера телефона задать предустановленную схему номера в формате пригодному для русскоязычного сегмента

+7 xxx-xxx-xx-xx (или вариант через 8-ку)

Решение: Выполнять поставленную задачу мы будем на JavaScript, в первом случае — на базе библиотеки jQuery, которая поставляется с WordPress и доступна «из коробки» и во-втором — решение на нативном JS (то есть без зависимостей от сторонних библиотек, в частности jQuery)

Мне встретилось в интернете уже готовое решение, поэтому придерживаясь принципа: «Зачем писать то, что уже написано», я взял его за пример. Единственное, нужно проинспектировать нашу форму отправления заказа и выяснить идентификатор нашего «инпута» для указания номера телефона.

Давайте взглянем на форму при оформлении заказа в WooCommerce.

Если требуется добавить в поле подсказку какой формат ввода номера телефона мы ожидаем от пользователя, то поможет нам в этом специальный фильтр, который отвечает за возможность изменения информации в полях. В документации есть подходящий для решения нашей под-задачи пример

// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );

// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
    $fields['order']['order_comments']['placeholder'] = 'My new placeholder';
    $fields['order']['order_comments']['label']       = 'My new label';
    return $fields;
}

	

В примерах из документации можно найти все названия полей. В нашем случае идентификатор нашего поля billing_phone (смотреть рисунок выше, с исследованием элемента) и он относится к группе Billing (эта информация получена тут). Поэтому наш код будет выглядеть следующим образом:

// Hook in
add_filter( 'woocommerce_checkout_fields', 'poet_override_checkout_fields' );
   
function poet_override_checkout_fields( $fields ) {
    $fields['billing']['billing_phone']['placeholder'] = '+7 (123) 456-78-90';
    $fields['billing']['billing_phone']['maxlength'] = 10; // ожидаем ввода 10 основных символов номера телефона
    return $fields;
}

Выполнение данного кода привнесло в поле нашей формы подсказку-заполнитель

placeholder

Естественно, мы не можем ручаться что именно так будет осуществлён ввод данных пользователем. Поэтому напишем вторую функцию, которая будет задавать правильное форматирование при вводе благодаря шаблону, который мы реализуем.

В WooCommerce для этого есть удобная функция wc_enqueue_js(), которая добавляет пользовательский код в вывод в нижней части сайта перед закрывающим тегом </body>. Вывод справедлив если страница каким то образом относится к WooCommerce. То есть на одиночной странице записи этого вывода не будет.

Вывод скрипта

Найти функцию можно от корня вашего сайта в wp-contents/plugins/woocommerce/includes/wc-core-functions.php

plugins/woocommerce/includes/wc-core-functions.php

Она позволяет добавлять в глобальную переменную (global $wc_queued_js) произвольный код. А её вызов можно увидеть чуть ниже по коду:

Обратите внимание, что обёртка для jQuery уже присутствует и можно просто пользоваться её методами. И вот изящное решение, от Rodolfo Melogli можно увидеть ниже.

/**
 * @snippet       Phone Mask @ WooCommerce Checkout
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli
 * @compatible    WooCommerce 5
 * @community     https://businessbloomer.com/club/
 */
 
add_filter( 'woocommerce_checkout_fields', 'bbloomer_checkout_phone_us_format' );
   
function bbloomer_checkout_phone_us_format( $fields ) {
    $fields['billing']['billing_phone']['placeholder'] = '123-456-7890';
    $fields['billing']['billing_phone']['maxlength'] = 12; // 123-456-7890 is 12 chars long
    return $fields;
}
 
add_action( 'woocommerce_after_checkout_form', 'bbloomer_phone_mask_us' );
 
function bbloomer_phone_mask_us() {
   wc_enqueue_js( "
      $('#billing_phone')
      .keydown(function(e) {
         var key = e.which || e.charCode || e.keyCode || 0;
         var phone = $(this);       
         if (key !== 8 && key !== 9) {
           if (phone.val().length === 3) {
            phone.val(phone.val() + '-'); // add dash after char #3
           }
           if (phone.val().length === 7) {
            phone.val(phone.val() + '-'); // add dash after char #7
           }
         }
         return (key == 8 ||
           key == 9 ||
           key == 46 ||
           (key >= 48 && key <= 57) ||
           (key >= 96 && key <= 105));
        });
         
   " );
}

Оно рабочее. Но зависит от библиотеки jQuery и «заточено» под американский стандарт, так сказать. Так же данное решение не сработает на страницах, которые не относятся к WooCommerce (справедливо для данного кода выше). Ещё мне не нравится пример заполнителя, пользовательский опыт говорит об обратном, вряд ли все будут придерживаться строгого заполнения.

Можно конечно доработать и всё исправить (в случае с примером выше основанным на jQuery), но мне больше понравилась библиотека Cleave.js и её аддон, который заточен под русские стандарты при вводе номера телефона, учитывая с «восьмёрки» или с «плюс, семёрки» начинает набирать пользователь номер.

Поэтому в данном примере мы подключим саму библиотеку и её аддон для страны с кодом RU (в целях уменьшения размера подключаемого кода) и проверим его работу в действии.

К тому же, подключать мы будет стандартной функцией WP для того чтобы решение было универсальным и могло использоваться на разных страницах к разным формам, в том числе популярного плагина Contact Form 7.

Важное примечание! Библиотека с минувшей осени (обращение автора от 25.11.23г) уже не поддерживается по ряду причин, которые описаны в обращении. Она переписана автором на TypeScript и ES6. Пример в статье просто иллюстрирует решение задачи. В целом рекомендуется эта библиотека.

Первым делом задействуем старую добрую функцию WP wp_enqueue_script(), которая будет подключать скрипт библиотеки Cleave.js и её аддон ориентированный на RU. Проверка ! is_checkout() гарантирует подключение только на странице оформления заказа и исключает подключение на прочих страницах

/**
 * Подключаем библиотеку Cleave.js
 * для форматирования содержимого <input/> при вводе текста
 */
function poet_load_scripts() {

	if ( ! is_checkout() ) {
		return;
	}

	wp_enqueue_script(
		'cleave',
		'https://cdn.jsdelivr.net/npm/cleave.js@1.6.0/dist/cleave.min.js',
		array(),
		'1.6.0',
		array(
			'strategy'  => 'defer',
			'in_footer' => true
		)
	);

	wp_enqueue_script(
		'cleave-phone',
		'https://cdn.jsdelivr.net/npm/cleave.js@1.6.0/dist/addons/cleave-phone.ru.js',
		array( 'cleave' ),
		'1.6.0',
		array(
			'strategy'  => 'defer',
			'in_footer' => true
		)
	);

	wp_add_inline_script( 'cleave-phone', "
		const cleave = new Cleave('#billing_phone', {
		    phone: true,
		    phoneRegionCode: 'ru',
		    delimiter: '-'
		});
	" );
	
}

add_action( 'wp_enqueue_scripts', 'poet_load_scripts' );

wp_add_inline_script( ‘cleave-phone’… инициализирует (вызывает функционал) на нужном нам поле с нужными параметрами, а частности разделителем в виде чёрточки.

Результатом выполнения кода будет предопределённая маска, которая не позволит пользователю вводит через чур много символов и будет учитывать начало ввода «+7» или «8»

ввод с +7
ввод с 8

Как подключить маску на других страницах, к другим формам?

Всё очень просто. Решение универсальное в пределах WP.

  1. У нужной формы необходимо понять, как «зацепиться» за нужное поле, или «по айди» или «по классу»
  2. Узнать «айди» страницы, для того чтобы добавить её в условие проверки, дабы не вызывать где не попадя 🙂

Например, у нас есть форма для обратной связи на странице контактов. Первым делом исследуем нужное поле:

Видим подходящий класс. Следовательно, понимаем, как будем делать «селект» — по классу. Далее, узнаем «айдишник» страницы на которой наша форма. В итоге наш код может выглядеть так:

/**
 * Подключаем библиотеку Cleave.js
 * для форматирования содержимого <input/> при вводе текста
 */
function poet_load_scripts() {

	if ( ! is_checkout() && ! is_page( 53 ) ) {
		return;
	}

	wp_enqueue_script(
		'cleave',
		'https://cdn.jsdelivr.net/npm/cleave.js@1.6.0/dist/cleave.min.js',
		array(),
		'1.6.0',
		array(
			'strategy'  => 'defer',
			'in_footer' => true
		)
	);

	wp_enqueue_script(
		'cleave-phone',
		'https://cdn.jsdelivr.net/npm/cleave.js@1.6.0/dist/addons/cleave-phone.ru.js',
		array( 'cleave' ),
		'1.6.0',
		array(
			'strategy'  => 'defer',
			'in_footer' => true
		)
	);

	wp_add_inline_script( 'cleave-phone', "
		const cleave = new Cleave('#billing_phone, .wpcf7-tel', {
		    phone: true,
		    phoneRegionCode: 'ru',
		    delimiter: '-'
		});
	" );

}

add_action( 'wp_enqueue_scripts', 'poet_load_scripts' );

Из примера видно, что мы просто, через запятую, добавили новый селектор по CSS классу. Теперь наша маска применяется к полу телефона созданной плагином Contact Form 7

маска на CF7

Как сделать копирование артикула товара по клику в WooCommerce?

Задача: для удобства сотрудников сделать копирование артикула по клику.

Реализовывать мы будем это на стороне клиента (браузера) при помощи JavaScript используя нужные нам методы при работе с DOM.

Давайте рассуждать. У нас есть артикул, значение которого выводится в строчном элементе <span>, благодаря атрибуту class мы можем «зацепиться за него» — это, условно говоря, его имя.

sku

Так как нам нужно только его значение, отметим для себя элемент span с классом «sku»

Нам нужно его выдернуть из DOM (Объектная Модель Документа) и в этом нам поможет метод querySelector()

Согласитесь, что нашего элемента может на странице и не существовать, поэтому нужно проверить его существование. Если совпадений не будет найдено, то наш метод вернёт значение null.

Давайте реализуем эту логику в виде кода.

const sku = document.querySelector(".sku");

if(sku !== null){
    console.log(sku);
}

Если вы всё сделали правильно, то непременно увидите в консоли этот выбранный элемент:

Следующим логическим шагом нам нужно будет отследить событие клика по этому элементу. В этом нам поможет метод

addEventListener() который будет обрабатывать событие клика по этому элементу

const sku = document.querySelector(".sku");

if (sku !== null) {
    sku.addEventListener("click", (e) => {

    });
}

Итак, событие клика мы отслеживаем и теперь настало время вызвать в момент клика нашу функцию, которая и будет осуществлять копирование содержимого.

Функцию назовём copyWooSKU()

Что должно находиться в функции? Очевидно, что нам нужно получить (зачитать) значение артикула, а затем его каким то чудесным образом скопировать в буфер обмена пользователя. Реализовывать мы это будем через современный API Async Clipboard (на замену синхронного метода document.execCommand())

Clipboard API предоставляет возможность реагировать на команды буфера обмена (вырезать, копировать и вставить), а также выполнять асинхронные чтение/запись в системный буфер обмена.

https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API

Итак, нам понадобится:

  1. Объект Navigator
  2. Свойство этого объекта .clipboard — которое используется для чтения и записи содержимого буфера обмена
  3. И метод writeText() — для записи текста в буфер обмена
async function copyWooSKU(txt) {
    try {
        await navigator.clipboard.writeText(txt);
    } catch (error) {
        console.error("Ошибочка вышла: ", error);
    }
}

const sku = document.querySelector(".sku");

if (sku !== null) {
    sku.addEventListener("click", (e) => {
        copyWooSKU(e.target.innerText);
    });
}

Ну вот и всё! Это должно работать 🙂

Обращу внимание, что стрелочные функции не имеют собственного контекста this. То есть в стрелочных функциях

e.currentTarget != this

Комментарий к коду:

  • async перед функцией, потому что в теле функции есть await и вернётся промис
  • try / catch для обработки исключения
  • e.target.innerText — это извлечение значения свойства (текста в нашем случае) элемента по которому кликаем

Как и многие новые API, API буфера обмена поддерживается только для страниц, обслуживаемых по протоколу HTTPS. Чтобы предотвратить злоупотребления, доступ к буферу обмена разрешен только тогда, когда страница является активной вкладкой. Страницы на активных вкладках могут записывать в буфер обмена без запроса разрешения, но чтение из буфера обмена всегда требует разрешения.

Учтите, что метод writeText() ожидает безопасного контекста HTTPS и если ваше соединение осуществляется по схеме протокола HTTP то может вылететь как раз таки ошибочка 🙂

Рекомендуется для чтения:

Разблокирование доступа к буферу обмена

Как добавить уведомление?

Очевидно, что в момент клика непонятно, произошло копирование или нет. Если улучшать наш код для пользователя, то по доброму необходимо сделать уведомление, чтобы клиенту было понятно, что копирование осуществилось.

Давайте рассуждать. Нам нужно методами JavaScript создать элемент в котором будет содержаться текст нашего уведомления. После вызова функции необходимо отобразить это уведомление и спустя пару секунд скрыть. Весь наш код теперь выглядит следующим образом:



async function copyWooSKU(txt) {
    try {
        await navigator.clipboard.writeText(txt);
    } catch (error) {
        console.error("Ошибочка вышла: ", error);
    }
}

const sku = document.querySelector(".sku");

if (sku !== null) {
    sku.addEventListener("click", (e) => {

        e.preventDefault();
        copyWooSKU(e.target.innerText);

        const myNotice = document.createElement("span");
        myNotice.style.color = "red";
        myNotice.innerText = "Скопировано";

        // селектор родителя
        if (sku.parentElement) {
          const parentElement = sku.parentElement;
          parentElement.appendChild(myNotice);

          window.setTimeout(function () {
            parentElement.removeChild(myNotice);
          }, 2000);
        } // TODO else 

    });
}

В этом коде использован следующий JavaScript:

  • Метод preventDefault () — добавил на всякий случай для отмены обработки клика (до нашего обработчика)
  • document.createElement() — метод для создания элемента
  • style.color — присваиваем CSS-свойство для изменения цвета шрифта
  • Свойство .innerText — позволяет задавать текст нашему созданному элементу
  • Метод .appendChild() добавляет узел в конец списка дочерних элементов указанного родительского узла. Будьте внимательны! Селектор родительского элемента у вас может отличаться.
  • Функция .setTimeout() — для задержки выполнения
  • Метод .removeChild() удаляет дочерний элемент из DOM.
  • parentElement это родительский элемент текущего узла. 

Возможно некоторые свойства, такие как : target, innerText станут понятнее, если мы на них взглянем. Для этого в этом отрезке кода, где мы отслеживаем действие по клику, можно вывести в консоль это событие клика мышкой:


if (sku !== null) {
    sku.addEventListener("click", (e) => 

        console.log(e);

    });
}

Мы увидим объект, и именно в нём и находятся наши свойства:

свойство таргет

А вот свойство со значением артикула, к которому мы обращаемся

Поиграть с рабочим кодом можно тут