CSS3-трансформации

Модель визуального форматирования CSS описывает систему координат внутри каждого позиционированного элемента. Система координат является точкой отсчета для свойств смещения. Положение и размеры в этом координатном пространстве можно рассматривать как заданные в пикселях, относительно точки отсчета, с положительными значениями, идущими вправо и вниз. Это координатное пространство можно изменить с помощью свойства transform.

CSS3-трансформации позволяют сдвигать, поворачивать и масштабировать элементы. Трансформации преобразовывают элемент, не затрагивая остальные элементы веб-страницы, т.е. другие элементы не сдвигаются относительно него.

В HTML трансформируемый элемент это: элемент с display: block; и display: inline-block;, а также элементы, значение свойства displayкоторых вычисляется как table-row, table-row-group, table-header-group, table-footer-group, table-cell или table-caption.

Трансформированным считается элемент с любым установленным значением свойства transform, отличным от none.

В SVG трансформируемый элемент — это элемент, который имеет атрибуты transform, patternTransform или gradientTransform.

Существуют два вида CSS3-трансформаций – 2Dи 3D. 2D-трансформации преобразовывают элементы в двумерном пространстве c помощью 2D-матрицы преобразований. Эта матрица применяется для вычисления новых координат объекта, на основе значений свойств transformи transform-origin. Преобразования влияют только на визуальный рендеринг. В отношении макета страницы они могут отразиться на переполнении содержимого блока. По умолчанию точка трансформации находится в центре элемента.

2D-трансформации элементов

1. Функции 2D-трансформации: свойство transform

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

Свойство не наследуется.

Функция

Описание

none

Значение по умолчанию, означает отсутствие трансформации. Также отменяет трансформацию для элемента из группы трансформируемых элементов.

matrix(a, c, b, d, x, y)

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

Значение aизменяет масштаб по горизонтали. Значение от 0 до 1 уменьшает элемент, больше 1 — увеличивает.

Значение cдеформирует (сдвигает) стороны элемента по оси Y, положительное значение — вверх, отрицательное — вниз.

Значение bдеформирует (сдвигает) стороны элемента по оси X, положительное значение — влево, отрицательное — вправо.

Значение dизменяет масштаб по вертикали. Значение меньше 1 уменьшает элемент, больше 1 — увеличивает.

Значение xсмещает элемент по оси X, положительное — вправо, отрицательное — влево.

Значение yсмещает элемент по оси Y, положительное значение — вниз, отрицательное — вверх.

translate(x,y)

Сдвигает элемент на новое место, перемещая относительно обычного положения вправо и вниз, используя координаты X и Y, не затрагивая при этом соседние элементы. Если нужно сдвинуть элемент влево или вверх, то нужно использовать отрицательные значения.

translateX(n)

Сдвигает элемент относительно его обычного положения по оси X.

translateY(n)

Сдвигает элемент относительно его обычного положения по оси Y.

scale(x,y)

Масштабирует элементы, делая их больше или меньше. Значения от 0 до 1 уменьшают элемент. Первое значение масштабирует элемент по ширине, второе — по высоте. Отрицательные значения отображают элемент зеркально.

scaleX(n)

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

scaleY(n)

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

rotate(угол)

Поворачивает элементы на заданное количество градусов, отрицательные значения от -1degдо -360degповорачивают элемент против часовой стрелки, положительные — по часовой стрелке. Значение rotate(720deg)поворачивает элемент на два полных оборота.

skew(x-угол,y-угол)

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

skewX(угол)

Деформирует стороны элемента относительно оси X.

skewY(угол)

Деформирует стороны элемента относительно оси Y.

initial

Устанавливает значение свойства в значение по умолчанию.

inherit

Наследует значение свойства от родительского элемента.

Допустимые значения:

matrix()— любое число
translate(), translateX(), translateY()— единицы длины (положительные и отрицательные), %
scale(), scaleX(), scaleY()— любое число
rotate()— угол (deg, grad, radили turn)
skew(), skewX(), skewY()— угол (deg, grad, rad)

Можно объединить несколько трансформаций одного элемента, перечислив их через пробел в порядке проявления.

Синтаксис

transform: none;
transform: matrix(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
transform: rotate(45deg);
transform: translate(12px, 50%);
transform: translateX(2em);
transform: translateY(3in);
transform: scale(2, 0.5);
transform: scaleX(2);
transform: scaleY(0.5);
transform: skew(30deg, 20deg);
transform: skewX(30deg);
transform: skewY(1.07rad);
transform: translateX(10px) rotate(10deg) translateY(5px);
transform: inherit;
transform: initial;

2. Точка трансформации: свойство transform-origin

Свойство transform-origin позволяет сместить центр трансформации, относительно которого происходит изменение положения/размера/формы элемента. Значение по умолчанию — center, или 50% 50%. Задаётся только для трансформированных элементов.

Свойство не наследуется.

transform-origin

Значения:

ось Х(left, center, right, длина, %)

ось Y(top, center, bottom, длина, %)

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

initial

Устанавливает значение свойства в значение по умолчанию.

inherit

Наследует значение свойства от родительского элемента.

Синтаксис

transform-origin: 2px;
transform-origin: bottom;
transform-origin: 3cm 2px;
transform-origin: left 2px;
transform-origin: right top;
transform-origin: inherit;
transform-origin: initial;

3. Область преобразования: свойство transform-box

Все преобразования, определяемые свойством transform и transform-origin, относятся к положению и размерам опорного блока элемента. Опорный блок элемента это виртуальный прямоугольник вокруг элемента, который формирует систему координат для отрисовки.

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

Опорный блок добавляет дополнительное смещение к исходной точке, заданной свойством transform-origin.

Свойство не наследуется.

transform-box

Значения:

content-box

В качестве опорного блока выступает область содержимого элемента. Для элемента <table>эта область включает также заголовок таблицы и рамку.

border-box

В качестве опорного блока выступает область рамки элемента. Для элемента <table>эта область включает также заголовок таблицы и рамку.

fill-box

В качестве опорного блока выступает ограничивающая рамка, содержащая только геометрическую форму элемента.

stroke-box

В качестве опорного блока выступает ограничивающая рамка, содержащая геометрическую форму и обводку элемента.

view-box

В качестве опорного блока используется область просмотра на SVG-холсте, которая определяет прямоугольную область, в которую отображается содержимое SVG.

initial

Устанавливает значение свойства в значение по умолчанию.

inherit

Наследует значение свойства от родительского элемента.

Синтаксис

transform-box: content-box;
transform-box: border-box;
transform-box: fill-box;
transform-box: stroke-box;
transform-box: view-box;
transform-box: inherit;
transform-box: initial;

jQuery Masked Input Plugin. Маска ввода для HTML элемента input

Для HostCMS

Подключаем в шапке сайта   ->js('/bootstrap/js/jquery.maskedinput.min.js')

$("input[name=phone]").mask("+7 (999) 999-9999");

Назначение плагина masked input

Плагин masked input предназначен для установления маски ввода элементу input с помощью кода JavaScript. Данный плагин для своего функционирования требует наличие подключённой библиотеки jQuery. Скачать плагин jquery.maskedinput.js (jquery.maskedinput.min) можно посредством следующей ссылки:
Подключение плагина
После того как Вы скачали этот плагин (файл js), его необходимо подключить. Это осуществляется с помощью элемента script:
  1. <!-- Подключение библиотеки jQuery -->
    <script src="jquery.js"></script>
    <!-- Подключение jQuery плагина Masked Input -->
    <script src="jquery.maskedinput.min.js"></script>

Создание HTML маски ввода

Создания маски ввода осушествляется в js коде с помощью следующих знаков:
  • Цифра 9 – соответствует цифре от 0 до 9.
  • Символ a – представляет собой любой английский символ (A-Z, a-z).
  • Знак * - представляет собой любой алфавитно-цифровой символ (A-Z, a-z, 0-9).
Например рассмотрим процесс создания маски ввода телефона для элемента input, имеющего id="phone":
<input id="phone" type="text">
<script>
//Код jQuery, установливающий маску для ввода телефона элементу input
//1. После загрузки страницы, когда все элементы будут доступны выполнить...
$(function(){
//2. Получить элемент, к которому необходимо добавить маску
$("#phone").mask("8(999) 999-9999");
});
</script>
Создание HTML элементу input маски ввода телефона
Пример 1:    8(945) 64_-_____
Внимание: По умолчанию в качестве заполнителя маски используется знак нижнего подчеркивания ('_').
Если в качестве заполнителя Вы хотите использовать что-то другое, то его можно указать посредством параметра placeholder следующим образом:
<!--HTML элемент, который будет иметь заполнитель дд.мм.гггг -->
<input id="date" type="text">
<!--HTML элемент, который будет иметь в качестве заполнителя пробел -->
<input id="index" type="text">
<script>
 $(function() {
 //задание заполнителя с помощью параметра placeholder
$("#date").mask("99.99.9999", {placeholder: "дд.мм.гггг" });
 //задание заполнителя с помощью параметра placeholder
$("#index").mask("999999", {placeholder: " " });
 });
 </script>
Использование различных заполнителей в масках ввода masked input
Пример 2:  15.мм.гггг
Кроме placeholder данный плагин имеет ещё параметр completed. Он предназначен для задания действий, которые будут выполнятся после того как пользователь завершит ввод маски ввода.
Например, выведем с помощью метода alert сообщение пользователю, когда он завершит ввод маски телефона:
<!-- Ввод номера телефона осуществляется с помощью маски -->
 <input id="phone" type="text">
 <script>
 $(function(){
//Использование параметра completed
 $("#phone").mask("8(999) 999-9999", {
completed: function(){ alert("Вы ввели номер: " + this.val()); }
 });
});
</script>
Сообщение, отображаемое пользователю после того как он завершил ввод маски телефона
Пример 3: 6(945) 343-7667
Подтвердите действие на sites.ru
Вы ввели номер: 6(945) 343-7667
Иногда бывают такие ситуации, когда одна часть маски является обязательной для заполнения, а другая часть нет. Чтобы это указать, в Masked Input используется знак '?'. Этот знак является специальным символом, после которого необходимо разместить часть маски необязательной для заполнения.
Например, пользователю необходимо ввести число от 0 до 0.99. При этом обязательным для заполнения является указание хотя бы одного знака после запятой.
<!-- Ввод номера телефона осуществляется с помощью маски -->
<input id="number" type="text">
<script>
jQuery(function($){
 //создания своего специального символа для маски
$("#number").mask("0.9?9");
 });
</script>
Демонстрация работы маски для ввода числа с одним или двумя знаками после запятой
Пример 4:  0.2

Настройка маски ввода Masked Input

Плагин Masked Input позволяет использовать в маске кроме предопределенных специальных знаков (9a*) свои собственные.
Например, создадим для маски специальный символ ~, который при вводе должен быть заменён на знак (+) или минус (-).
<!-- HTML элемент, имеющий маску телефона -->
<input id="number" type="text">
<script>
jQuery(function($){
//создания специального символа для маски
$.mask.definitions['~']='[+-]';
$("#number).mask("~9.99");
});
</script>
Демонстрация работы маски для ввода положительного или отрицательного числа
Пример 5:   -5.3
Например, создадим маску для ввода CSS цвета в шестнадцатеричном формате:
<!-- HTML элемент, имеющий маску для ввода цвета в шестнадцатиричном формате -->
<input id="color" type="text">
<script>
jQuery(function($){
//создания специального символа h для маски
$.mask.definitions['h']='[A-Fa-f0-9]';
$("#color).mask("#hhhhhh");
});
</script>
Демонстрация работы маски для ввода цвета CSS в шестандцатиричном формате
Пример 6:   #675a_

Пример создания маски ввода телефона

Рассмотрим пример создания маски для ввода телефона в зависимости от выбранной страны:
<div class="form-group">
<label for="phone">Телефон: </label>
<select id="country" class="form-control">
<option value="ru"><img src="">Россия +7</option>
<option value="ua">Украина +380</option>
<option value="by">Белоруссия +375</option>
</select>
<input id="phone" type="text" class="form-control">
</div>

<script>
jQuery (function ($) {
$(function() {
function maskPhone() {
var country = $('#country option:selected').val();
switch (country) {
case "ru":
$("#phone").mask("+7(999) 999-99-99");
break;
case "ua":
$("#phone").mask("+380(999) 999-99-99");
break;
case "by":
$("#phone").mask("+375(999) 999-99-99");
break;
}
}
maskPhone();
$('#country').change(function() {
maskPhone();
});
});
});
</script>
Демонстрация работы маски для ввода телефона в зависимости от выбранной страны
Пример 7
Телефон:
Белоруссия +375  +375(777) 6__--

JavaScript / jQuery . jquery.maskedinput.min.js

/*
    jQuery Masked Input Plugin
    Copyright (c) 2007 - 2015 Josh Bush (digitalbush.com)
    Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
    Version: 1.4.1
*/
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b,c=navigator.userAgent,d=/iphone/i.test(c),e=/chrome/i.test(c),f=/android/i.test(c);a.mask={definitions:{9:"[0-9]",a:"[A-Za-z]","*":"[A-Za-z0-9]"},autoclear:!0,dataName:"rawMaskFn",placeholder:"_"},a.fn.extend({caret:function(a,b){var c;if(0!==this.length&&!this.is(":hidden"))return"number"==typeof a?(b="number"==typeof b?b:a,this.each(function(){this.setSelectionRange?this.setSelectionRange(a,b):this.createTextRange&&(c=this.createTextRange(),c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",a),c.select())})):(this[0].setSelectionRange?(a=this[0].selectionStart,b=this[0].selectionEnd):document.selection&&document.selection.createRange&&(c=document.selection.createRange(),a=0-c.duplicate().moveStart("character",-1e5),b=a+c.text.length),{begin:a,end:b})},unmask:function(){return this.trigger("unmask")},mask:function(c,g){var h,i,j,k,l,m,n,o;if(!c&&this.length>0){h=a(this[0]);var p=h.data(a.mask.dataName);return p?p():void 0}return g=a.extend({autoclear:a.mask.autoclear,placeholder:a.mask.placeholder,completed:null},g),i=a.mask.definitions,j=[],k=n=c.length,l=null,a.each(c.split(""),function(a,b){"?"==b?(n--,k=a):i[b]?(j.push(new RegExp(i[b])),null===l&&(l=j.length-1),k>a&&(m=j.length-1)):j.push(null)}),this.trigger("unmask").each(function(){function h(){if(g.completed){for(var a=l;m>=a;a++)if(j[a]&&C[a]===p(a))return;g.completed.call(B)}}function p(a){return g.placeholder.charAt(a<g.placeholder.length?a:0)}function q(a){for(;++a<n&&!j[a];);return a}function r(a){for(;--a>=0&&!j[a];);return a}function s(a,b){var c,d;if(!(0>a)){for(c=a,d=q(b);n>c;c++)if(j[c]){if(!(n>d&&j[c].test(C[d])))break;C[c]=C[d],C[d]=p(d),d=q(d)}z(),B.caret(Math.max(l,a))}}function t(a){var b,c,d,e;for(b=a,c=p(a);n>b;b++)if(j[b]){if(d=q(b),e=C[b],C[b]=c,!(n>d&&j[d].test(e)))break;c=e}}function u(){var a=B.val(),b=B.caret();if(o&&o.length&&o.length>a.length){for(A(!0);b.begin>0&&!j[b.begin-1];)b.begin--;if(0===b.begin)for(;b.begin<l&&!j[b.begin];)b.begin++;B.caret(b.begin,b.begin)}else{for(A(!0);b.begin<n&&!j[b.begin];)b.begin++;B.caret(b.begin,b.begin)}h()}function v(){A(),B.val()!=E&&B.change()}function w(a){if(!B.prop("readonly")){var b,c,e,f=a.which||a.keyCode;o=B.val(),8===f||46===f||d&&127===f?(b=B.caret(),c=b.begin,e=b.end,e-c===0&&(c=46!==f?r(c):e=q(c-1),e=46===f?q(e):e),y(c,e),s(c,e-1),a.preventDefault()):13===f?v.call(this,a):27===f&&(B.val(E),B.caret(0,A()),a.preventDefault())}}function x(b){if(!B.prop("readonly")){var c,d,e,g=b.which||b.keyCode,i=B.caret();if(!(b.ctrlKey||b.altKey||b.metaKey||32>g)&&g&&13!==g){if(i.end-i.begin!==0&&(y(i.begin,i.end),s(i.begin,i.end-1)),c=q(i.begin-1),n>c&&(d=String.fromCharCode(g),j[c].test(d))){if(t(c),C[c]=d,z(),e=q(c),f){var k=function(){a.proxy(a.fn.caret,B,e)()};setTimeout(k,0)}else B.caret(e);i.begin<=m&&h()}b.preventDefault()}}}function y(a,b){var c;for(c=a;b>c&&n>c;c++)j[c]&&(C[c]=p(c))}function z(){B.val(C.join(""))}function A(a){var b,c,d,e=B.val(),f=-1;for(b=0,d=0;n>b;b++)if(j[b]){for(C[b]=p(b);d++<e.length;)if(c=e.charAt(d-1),j[b].test(c)){C[b]=c,f=b;break}if(d>e.length){y(b+1,n);break}}else C[b]===e.charAt(d)&&d++,k>b&&(f=b);return a?z():k>f+1?g.autoclear||C.join("")===D?(B.val()&&B.val(""),y(0,n)):z():(z(),B.val(B.val().substring(0,f+1))),k?b:l}var B=a(this),C=a.map(c.split(""),function(a,b){return"?"!=a?i[a]?p(b):a:void 0}),D=C.join(""),E=B.val();B.data(a.mask.dataName,function(){return a.map(C,function(a,b){return j[b]&&a!=p(b)?a:null}).join("")}),B.one("unmask",function(){B.off(".mask").removeData(a.mask.dataName)}).on("focus.mask",function(){if(!B.prop("readonly")){clearTimeout(b);var a;E=B.val(),a=A(),b=setTimeout(function(){B.get(0)===document.activeElement&&(z(),a==c.replace("?","").length?B.caret(0,a):B.caret(a))},10)}}).on("blur.mask",v).on("keydown.mask",w).on("keypress.mask",x).on("input.mask paste.mask",function(){B.prop("readonly")||setTimeout(function(){var a=A(!0);B.caret(a),h()},0)}),e&&f&&B.off("input.mask").on("input.mask",u),A()})}})});

Загрузка изображений через форму

Показываем превью загружаемых изображений

<label for="fileUpload" class="download-file">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="25px" height="21px" version="1.1" viewBox="0 0 6.01 5.01"><path style="fill:#764E2B" d="M6.01 5.01l-6.01 0 0 -5.01 6.01 0 0 5.01 0 0zm-0.25 -4.76l-5.51 0 0 4.5 5.51 0 0 -4.5 0 0zm-0.25 4l-4.76 0 1 -1.87 0.75 0.76 1.26 -1.89 1.75 3zm-2.97 -0.71l-0.73 -0.74 -0.64 1.2 3.9 0 -1.33 -2.28 -1.2 1.82zm-1.16 -2.79c0.34,0 0.62,0.28 0.62,0.63 0,0.34 -0.28,0.62 -0.62,0.62 -0.35,0 -0.63,-0.28 -0.63,-0.62 0,-0.35 0.28,-0.63 0.63,-0.63zm0 0.25c0.2,0 0.37,0.17 0.37,0.38 0,0.2 -0.17,0.37 -0.37,0.37 -0.21,0 -0.38,-0.17 -0.38,-0.37 0,-0.21 0.17,-0.38 0.38,-0.38z"></path></svg>
<span class="download-file-text ps-2">Выберите несколько фото</span>
</label>
<input class="photo" type="file" name="files[]" value="" size="30" placeholder="Загрузить фото" multiple="true" accept="image" id="fileUpload" accepts=".jpg, .jpeg, .png, .webp">
<div id="image-holder" class="d-flex flex-wrap"></div>

JS для размещения в XSL-шаблон HOSTCMS

<SCRIPT>
    <xsl:comment>
    <xsl:text disable-output-escaping="yes">
    <![CDATA[

    $(function() {
    $("#fileUpload").on('change', function () {
 
     //Get count of selected files
     var countFiles = $(this)[0].files.length;
     var imgPath = $(this)[0].value;
     var extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase();
     var image_holder = $("#image-holder");
     image_holder.empty();
 
     if (extn == "png" || extn == "jpg" || extn == "jpeg" || extn == "webp") {
         if (typeof (FileReader) != "undefined") {
 
             //loop for each file selected for uploaded.
             for (var i = 0; i < countFiles; i++) {
 
                 var reader = new FileReader();
                 reader.onload = function (e) {
                     $("<img />", {
                         "src": e.target.result,
                             "class": "thumb-image"
                     }).appendTo(image_holder);
                 }
 
                 image_holder.show();
                 reader.readAsDataURL($(this)[0].files[i]);
             }
 
         } else {
             alert("Ваш брузер не поддерживает FileReader!");
         }
     } else {
         alert("Выберите файл в формате JPG, JPEG, PNG или WEBP!");
     }
    });
    });

    ]]>
    </xsl:text>
    </xsl:comment>
</SCRIPT>

Добавляем поле загрузки файла (новое поле создается после загрузки изображения)

<SCRIPT> 
    function addOneMoreInput() {
    $('input[type=file]').last().change(function() {
        $(this).after('<input type="file" name="files[]" />');
        $(this).off('change');
        addOneMoreInput();
    });
   }
   addOneMoreInput();
</SCRIPT>

Стили CSS

.download-file {
cursor: pointer
}
#image-holder {
margin-top: 10px
}
#image-holder img {
margin: 2px;
border: 2px solid rgba(120,79,43,0.5);
max-height: 50px !important
}
input.photo {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
border: 0;
padding: 0;
clip: rect(0 0 0 0);
overflow: hidden
}
.form-check, .download-file-text {
color: #764E2B;
font-size: 13px
}

Загрузка формы на страницу с помощью AJAX

Создаётся узел структуры - Обратная связь (/zayavka/)
Подключается ТДС "Отображение формы".
Кнопка вызова формы.
<button
    class="btn btn-secondary btn-xl"
    type="button"
    onclick="
    return
     $.openForm('/zayavka/', 18  // здесь меняем ID формы
    )"
>Заполнить заявку</button>

PHP . Код настроек ТДС "Отображение формы"

<?php
// Добавленный код
if (Core_Array::getRequest('openForm') == "1") 
ob_start(); 
$form_id = intval(Core_Array::getRequest('form_id')); 
// ФОРМА
$oForm = Core_Entity::factory('Form', $form_id); 
$Form_Controller_Show = new Form_Controller_Show($oForm); 
if (!is_null(Core_Array::getPost($oForm->button_name))) 
$Form_Controller_Show 
->values($_POST + $_FILES)
// 0 - html, 1- plain text 
->mailType(0) 
->mailXsl( 
Core_Entity::factory('Xsl')->getByName(Core_Array::get(Core_Page::instance()->libParams, 'notificationMailXsl'))
->mailFromFieldName(Core_Array::get(Core_Page::instance()->libParams, 'emailFieldName'))
->process(); 
// echo "<pre>";
    //       print_r($_POST + $_FILES);
    //    echo "</pre>";
$Form_Controller_Show 
->xsl( 
Core_Entity::factory('Xsl')->getByName(Core_Array::get(Core_Page::instance()->libParams, 'formXsl'))
->show(); 
echo json_encode(ob_get_clean()); 
exit(); 
}
// end Добавленный код
// Page doesn't accept subpages, 404 error
$oCore_Page = Core_Page::instance();
if ($oCore_Page->structure->getPath() != Core::$url['path'])
{
$oCore_Page->error404();
}

XSL . Шаблон "ОтобразитьФормуЗаявкаВМодальномОкне" с отправкой изображений

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet SYSTEM "lang://351">
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:hostcms="http://www.hostcms.ru/"
exclude-result-prefixes="hostcms">
<xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml" />
<!-- ОтобразитьФорму -->
<xsl:template match="/">
<xsl:apply-templates select="/form" />
</xsl:template>
<xsl:template match="/form">
<xsl:choose>
<xsl:when test="success/node() and success = 1">
<!--div id="fade" style="display: block;"></div-->
<div class="modal fade form-success-result" id="modalSuccess" tabindex="-1" role="dialog">
<div class="modal-dialog  modal-dialog-centered">
<div class="modal-content p-5">
<div class="modal-header text-center">
<!--div class="modal-title w-100 fs-4 fw-bold">&labelModalTitle1;</div-->
<button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<xsl:value-of disable-output-escaping="yes" select="success_text" />
</div>
</div>
</div>
</div>
<script>
let modalSuccess = new bootstrap.Modal(document.getElementById('modalSuccess'));
modalSuccess.show();
</script>
</xsl:when>
<!-- Выводим ошибку (error), если она была передана через внешний параметр -->
<xsl:when test="error != ''">
<div class="modal fade form-success-result" id="modalError" tabindex="-1" role="dialog">
<div class="modal-dialog  modal-dialog-centered">
<div class="modal-content p-5">
<div class="modal-header text-center">
<div class="modal-title w-100 fs-3 fw-bold">ОШИБКА!</div>
<button class=" " type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body"><b><xsl:value-of disable-output-escaping="yes" select="error" /></b></div>
</div>
</div>
</div>
<script>
let modalError = new bootstrap.Modal(document.getElementById('modalError'));
modalError.show();
</script>
</xsl:when>
<xsl:when test="errorId/node()">
<div class="modal fade form-success-result" id="modalError" tabindex="-1" role="dialog">
<div class="modal-dialog  modal-dialog-centered">
<div class="modal-content p-5">
<div class="modal-header text-center">
<div class="modal-title w-100 fs-3 fw-bold">&labelERROR;</div>
<button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<xsl:choose>
<xsl:when test="errorId = 0">
&labelTextErrorId0;
</xsl:when>
<xsl:when test="errorId = 1">
&labelTextErrorId1;
</xsl:when>
<xsl:when test="errorId = 2">
&labelTextErrorId2;
</xsl:when>
</xsl:choose>
</div>
</div>
</div>
</div>
<script>
let modalError = new bootstrap.Modal(document.getElementById('modalError'));
modalError.show();
</script>
</xsl:when>
<xsl:otherwise>
<div class="modal fade" id="applicationModal_{@id}" tabindex="-1" aria-labelledby="applicationModalLabel_{@id}" aria-hidden="true">
<div class="modal-dialog  modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header justify-content-center">
<div class="h2 m-0 modal-title" id="applicationModalLabel_{@id}"><xsl:value-of disable-output-escaping="yes" select="name" /></div>
<button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<xsl:if test="description !=''">
<xsl:value-of disable-output-escaping="yes" select="description" />
</xsl:if>
<div id="add-text" class="text-center"> </div>
<!-- Параметр action формы должен быть "./", если обработчик на этой же странице, либо "./form/", если обработчик на другой странице, например ./form/ -->
<form name="form{@id}" id="form{@id}" class="validate" action="./" method="post" enctype="multipart/form-data" >
<!-- Вывод разделов формы 0-го уровня -->
<xsl:apply-templates select="form_field_dir" />
<!-- Вывод списка полей формы 0-го уровня -->
<xsl:apply-templates select="form_field" />
<!-- Код подтверждения -->
<xsl:if test="captcha_id != 0">
<div class="form-group mb-3">
<div class="row">
<div class="col-6 caption">
<input type="hidden" name="captcha_id" value="{/form/captcha_id}"/>
<input type="text" name="captcha" size="15" class="form-control required" minlength="4" title="Введите число, которое указано на картинке."/>
<div class="padding-5">&labelCheckNumber1;<sup><font color="red">*</font></sup></div>
</div>
<div class="col-6 field">
<img id="formCaptcha_{/form/@id}_{/form/captcha_id}" src="/captcha.php?id={captcha_id}&amp;height=30&amp;width=100" class="captcha" name="captcha" />
<div class="captcha">
<img src="/images/refresh.png" /> <span class="small" onclick="$('#formCaptcha_{/form/@id}_{/form/captcha_id}').updateCaptcha('{/form/captcha_id}', 30); return false" style="cursor:pointer">&labelCheckNumber2;</span>
</div>
</div>
</div>
</div>
</xsl:if>
<!--div class="margin-bottom-10">
<div class="g-recaptcha" data-sitekey="6LfemXMiAAAAAPtn9tHaYj5z-0yKJq4Tmi3Ph1Cw"></div>
</div-->
<xsl:if test="csrf_token/node() and csrf_token != ''">
<input type="hidden" name="{csrf_field}" value="{csrf_token}" />
</xsl:if>
<div class="form-group">
<input type="hidden" name="{button_name}" value="submit"/>
<button id="submit_{@id}" value="submit" name="{button_name}" type="submit" class="btn btn-black mt-4 w-100"><xsl:value-of select="button_value" /></button>
</div>
</form>
<div class="mt-4">
<div class="form-check">
<input id="checkbox{@id}" class="form-check-input" type="checkbox" name="checkbox" checked="checked" onchange="document.getElementById('submit_{@id}').disabled = !this.checked;"/>
<label for="checkbox{@id}" class="form-check-label small" style="text-align:justify">Я соглашаюсь на обработку своих персональных данных в соответствии с <a href="/private" class="link-border"> Политикой конфиденциальности</a></label>
</div>
</div>
</div>
</div>
</div>
</div>
<SCRIPT>
<xsl:comment>
<xsl:text disable-output-escaping="yes">
    <![CDATA[
    $(function() {
    $("#fileUpload").on('change', function () {
 
     //Get count of selected files
     var countFiles = $(this)[0].files.length;
     var imgPath = $(this)[0].value;
     var extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase();
     var image_holder = $("#image-holder");
     image_holder.empty();
 
     if (extn == "png" || extn == "jpg" || extn == "jpeg" || extn == "webp") {
         if (typeof (FileReader) != "undefined") {
 
             //loop for each file selected for uploaded.
             for (var i = 0; i < countFiles; i++) {
 
                 var reader = new FileReader();
                 reader.onload = function (e) {
                     $("<img />", {
                         "src": e.target.result,
                             "class": "thumb-image"
                     }).appendTo(image_holder);
                 }
 
                 image_holder.show();
                 reader.readAsDataURL($(this)[0].files[i]);
             }
 
         } else {
             alert("Ваш брузер не поддерживает FileReader!");
         }
     } else {
         alert("Выберите файл в формате JPG, JPEG, PNG или WEBP!");
     }
    });
    });
]]>
</xsl:text>
</xsl:comment>
</SCRIPT>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="form_field_dir">
<!--fieldset class="maillist_fieldset"-->
<!--legend><xsl:value-of select="name" /></legend-->
<!-- Вывод списка полей формы -->
<xsl:apply-templates select="form_field" />
<!-- Вывод разделов формы -->
<xsl:apply-templates select="form_field_dir" />
<!--/fieldset-->
</xsl:template>
<xsl:template match="form_field">
<!-- Не скрытое поле и не надпись -->
<xsl:if test="type != 7 and type != 8">
<div class="form-group mb-3">
<xsl:if test="type = 2">
<xsl:attribute name="class">form-group mb-2 me-4</xsl:attribute>
</xsl:if>
<xsl:variable name="caption">
<xsl:choose>
<xsl:when test="obligatory = 1">
<xsl:value-of select="caption" />*
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="caption" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<!-- Радиокнопки -->
<xsl:when test="type = 3 or type = 9">
<xsl:apply-templates select="list/list_item" />
</xsl:when>
<!-- Checkbox -->
<xsl:when test="type = 4">
<div class="pretty p-default p-pulse">
<input type="checkbox" name="{name}">
<xsl:if test="checked = 1 or value = 1">
<xsl:attribute name="checked">checked</xsl:attribute>
</xsl:if>
</input>
<div class="state p-danger-o">
<label><xsl:value-of select="value" /></label>
</div>
</div>
</xsl:when>
<!-- Textarea -->
<xsl:when test="type = 5">
<textarea class="form-control" name="{name}" cols="{cols}" rows="{rows}" placeholder="{$caption}">
<xsl:if test="obligatory = 1">
<xsl:attribute name="class">form-control required</xsl:attribute>
</xsl:if>
<xsl:value-of select="value" />
</textarea>
</xsl:when>
<!-- Список -->
<xsl:when test="type = 6">
<select name="{name}" class="wide">
<xsl:if test="obligatory = 1">
<xsl:attribute name="class">wide required</xsl:attribute>
<xsl:attribute name="title"><xsl:value-of select="caption" /></xsl:attribute>
</xsl:if>
<option value="">...</option>
<xsl:apply-templates select="list/list_item" />
</select>
</xsl:when>
<!-- Текстовые поля -->
<xsl:otherwise>
<xsl:if test="type = 2">
<label for="fileUpload" class="download-file">
<i class="svg_icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="21px" height="21px" ><path style="fill:#797979" d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm16 336c0 8.822-7.178 16-16 16H48c-8.822 0-16-7.178-16-16V112c0-8.822 7.178-16 16-16h416c8.822 0 16 7.178 16 16v288zM112 232c30.928 0 56-25.072 56-56s-25.072-56-56-56-56 25.072-56 56 25.072 56 56 56zm0-80c13.234 0 24 10.766 24 24s-10.766 24-24 24-24-10.766-24-24 10.766-24 24-24zm207.029 23.029L224 270.059l-31.029-31.029c-9.373-9.373-24.569-9.373-33.941 0l-88 88A23.998 23.998 0 0 0 64 344v28c0 6.627 5.373 12 12 12h360c6.627 0 12-5.373 12-12v-92c0-6.365-2.529-12.47-7.029-16.971l-88-88c-9.373-9.372-24.569-9.372-33.942 0zM416 352H96v-4.686l80-80 48 48 112-112 80 80V352z"/></svg>
</i>
<span class="download-file-text ps-2">Выберите сразу несколько фото</span>
</label>
</xsl:if>
<input class="form-control" type="text" name="{name}" value="{value}" size="{size}" placeholder="{$caption}">
<xsl:choose>
<!-- Поле для ввода пароля -->
<xsl:when test="type = 1">
<xsl:attribute name="type">password</xsl:attribute>
</xsl:when>
<!-- Поле загрузки файла -->
<xsl:when test="type = 2">
<xsl:attribute name="type">file</xsl:attribute>
<xsl:attribute name="multiple">true</xsl:attribute>
<xsl:attribute name="class">photo</xsl:attribute>
<xsl:attribute name="name">files[]</xsl:attribute>
<xsl:attribute name="id">fileUpload</xsl:attribute>
<xsl:attribute name="accept">image/jpg,image/jpeg,image/png,image/webp</xsl:attribute>
<div id="image-holder" class="d-flex flex-wrap"></div>
</xsl:when>
<!-- HTML5: Дата -->
<xsl:when test="type = 10">
<xsl:attribute name="type">date</xsl:attribute>
</xsl:when>
<!-- HTML5: Цвет -->
<xsl:when test="type = 11">
<xsl:attribute name="type">color</xsl:attribute>
</xsl:when>
<!-- HTML5: Месяц -->
<xsl:when test="type = 12">
<xsl:attribute name="type">month</xsl:attribute>
</xsl:when>
<!-- HTML5: Неделя -->
<xsl:when test="type = 13">
<xsl:attribute name="type">week</xsl:attribute>
</xsl:when>
<!-- HTML5: Время -->
<xsl:when test="type = 14">
<xsl:attribute name="type">time</xsl:attribute>
</xsl:when>
<!-- HTML5: Дата-Время -->
<xsl:when test="type = 15">
<xsl:attribute name="type">datetime</xsl:attribute>
</xsl:when>
<!-- HTML5: E-mail -->
<xsl:when test="type = 16">
<xsl:attribute name="type">email</xsl:attribute>
</xsl:when>
<!-- HTML5: Поиск -->
<xsl:when test="type = 17">
<xsl:attribute name="type">search</xsl:attribute>
</xsl:when>
<!-- HTML5: Телефон -->
<xsl:when test="type = 18">
<xsl:attribute name="type">tel</xsl:attribute>
</xsl:when>
<!-- HTML5: URL -->
<xsl:when test="type = 19">
<xsl:attribute name="type">url</xsl:attribute>
</xsl:when>
<!-- Текстовое поле -->
<xsl:otherwise>
<xsl:attribute name="type">text</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="obligatory = 1">
<xsl:attribute name="class">form-control required</xsl:attribute>
</xsl:if>
<!--xsl:if test="@id = 15">
<xsl:attribute name="disabled">disabled</xsl:attribute>
</xsl:if-->
</input>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:if>
<!-- скрытое поле -->
<xsl:if test="type = 7">
<input type="hidden" name="{name}" value="{value}" />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
//
//
// Заменить в строке
// <!DOCTYPE xsl:stylesheet SYSTEM "lang://351">
// ID 351  на  ID своего SL шаблона 
//
// Вставить во вкладку ru.dtd
//

<!ENTITY labelModalTitle1 "Благодарим вас за заявку!">
<!ENTITY labelModalTitle2 "Заявка получена.">
<!ENTITY labelSuccessText "Мы свяжемся с вами в ближайшее время">
<!ENTITY labelSuccessText2 "Наш менеджер ответит на все ваши вопросы">

<!ENTITY labelERROR "ОШИБКА!">
<!ENTITY labelTextErrorId0 "Вы неправильно ввели код подтверждения отправки формы!">
<!ENTITY labelTextErrorId1 "Заполните все обязательные поля!">
<!ENTITY labelTextErrorId2 "С момента отправки последней формы прошло слишком мало времени!">

<!ENTITY labelTextError1 "Это поле пустое">
<!ENTITY labelTextError2 "E-mail должен иметь вид name@domain.com">

<!ENTITY labelText1 "Обязательное поле">

<!ENTITY labelCheckNumber1 "Проверочный код">
<!ENTITY labelCheckNumber2 "показать другой код">

JavaScript / jQuery . Размещаем в макете сайта. Из XSL шаблонов валидацию форм убрать

// Функции без создания коллекции
$.extend({
// Загрузка форм заявок на ajax с валидацией
openForm: function(path, form_id, form_title) {
if ($('.form-success-result').length) {
$('.form-success-result').remove();
}
if ($('#applicationModal_'+form_id).length) {
$('#applicationModal_'+form_id).modal('show').appendTo('body');
}
else {
$.clientRequest({
path: path + '?openForm=1&form_id=' + form_id,
'callBack': function(data, status, jqXHR){
$.openFormCallback(data, status, jqXHR, path + '?openForm=1&form_id=' + form_id, form_id, form_title)
},
context: ''
});
}
return false;
},
openFormCallback: function(data, status, jqXHR, path, form_id, form_title) {
$.loadingScreen('hide');
var Url = window.location.pathname;
$('body').append(data);
$('#applicationModal_'+form_id).modal('show').appendTo('body');
$('input[name=current_url]').val('https://site.ru'+Url);
$('input[name=form_title]').val(form_title);
$('form#form'+form_id).validate({
focusInvalid: true,
errorClass: "input_error",
onkeyup: false,
onfocusout: false,
rules: {
name: "required",
phone: "required",
email: {
required: true,
email: true
}
},
messages: {
name: "Это поле не заполнено",
phone: "Это поле не заполнено",
email: {
required: "Это поле не заполнено",
email: "E-mail должен иметь вид name@domain.ru"
}
},
submitHandler: function(form) {
var data = new FormData(form);
jQuery.ajax({
url: path,
type: 'POST',
data: data,
dataType: 'json',
processData : false,
contentType : false,
success: function(data) {
$("body").append(data);
if ($('.form-success-result').length) {
$('#applicationModal_'+form_id).modal('hide');
console.log(this);
$('.form-success-result').modal('show').appendTo('body');
}
}
});
return false;
}
});
},
loadingScreen: function(method) {
// Method calling logic
if (methods[method] ) {
return methods[method].apply(this, Array.prototype.slice.call( arguments, 1 ));
} else {
$.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
}
},
clientRequest: function(settings) {
if (typeof settings.callBack == 'undefined')
{
alert('Callback function is undefined');
}
$.loadingScreen('show');
var path = settings.path,
data = (typeof settings.data != 'undefined') ? settings.data : {};
data['_'] = Math.round(new Date().getTime());
jQuery.ajax({
context: settings.context,
url: path,
type: 'POST',
data: data,
dataType: 'json',
success: settings.callBack
});
return false;
}
});
var methods = {
show : function() {
$('body').css('cursor', 'wait');
var fade_div = $('#ajaxLoader'), jWindow = $(window);
if (fade_div.length === 0)
{
fade_div = $('<div></div>')
.appendTo(document.body)
.hide()
.prop('id', 'ajaxLoader')
.css('z-index', '1500')
.css('position', 'absolute')
.append($('<img>').prop('src', '/hostcmsfiles/images/ajax_loader.gif'));
}
fade_div.show()
.css('top', (jWindow.height() - fade_div.outerHeight(true)) / 2 + jWindow.scrollTop())
.css('left', (jWindow.width() - fade_div.outerWidth(true)) / 2 + jWindow.scrollLeft());
},
hide : function( ) {
$('#ajaxLoader').hide().css('left', -1000);
$('body').css('cursor', 'auto');
}
};

Загрузка формы на страницу с помощью AJAX для редакции HostCMS Старт

Создаётся узел структуры - Обратная связь (/zayavka/)
Тип раздела выбираем "Динамическая страница".
Кнопка вызова формы, которая размещается в макете на любой странице сайта.
<button 
    type="button" 
    class="btn btn-transparent btn-lg" 
   onclick="return $.openForm('/zayavka/', 'Вопрос на тему - Ремонт и выкуп элитных  швейцарских часов в Москве')" 
   tabindex="0">Задать вопрос
</button>
Добавляем на сервер в папку, например, /mform, обработчик отправки формы (в также него добавлена возможность отправки вложенных файлов) send-zakaz.php
<?php
$to  = "info@test.com";
$email = "zayavka@test.com";

if ( isset( $_POST['phone'] ) ) {
  $username= substr( $_POST['username'], 0, 40 );
  $phone = substr( $_POST['phone'], 0, 15 );
  $thema = substr( $_POST['thema'], 0, 250);
  $mess = substr( $_POST['mess'], 0, 250);

  $body = "Имя: ".$username."\r\n";
  $body  .= "Телефон: ".$phone."\r\n";
  $body  .= "Тема: ".$thema."\r\n\r\n";
  $body  .= "Сообщение: ".$mess."\r\n\r\n";

  send_mail($to, $body, $email);
}

// Вспомогательная функция для отправки почтового сообщения с вложением
function send_mail($to, $body, $email)
{
  $subject = 'Пользователь сайта test.com заполнил форму Заявки';
  $boundary = "--".md5(uniqid(time())); // генерируем разделитель
  $headers .= "From: ".$email."\r\n";   
  $headers .= "MIME-Version: 1.0\r\n";
  $headers .="Content-Type: multipart/mixed; boundary=\"".$boundary."\"\r\n";
  $multipart = "--".$boundary."\r\n";
  $multipart .= "Content-type: text/plain; charset=\"utf-8\"\r\n";
  $multipart .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";


  $body = $body."\r\n\r\n";
  $multipart .= $body;


  mail($to, $subject, $multipart, $headers);
}
?>

В Основном макете, в самом низу, добавляем код модального окна, в которое будет загружаться наша форма

    <div class="modal fade" id="applicationModal" tabindex="-1" aria-labelledby="applicationModalLabel" aria-modal="true" role="dialog">
        <div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
            <div class="modal-content">
                <div class="modal-header justify-content-center">
                    <div class="h4 m-0 modal-title" id="applicationModalLabel">Заявка</div>
                    <button class="btn-close" type="button" data-bs-dismiss="modal"></button>
                </div>

                <div id="modalContent" class="modal-body"></div>
            </div>
        </div>           
     </div>

PHP . В поле "Динамическая страница" вставляем следующий код формы

    <div id="formUpload">

        <form method="get" id="form2" action="./">
                    <input type="hidden" name="thema" id="thema_2">
                    <div class="mb-3">
                        <input type="text" name="username" class="form-control" maxlength="40" placeholder="Ваше Имя *">
                    </div>
                    <div class="mb-3">
                        <input name="phone" class="form-control" maxlength="15" placeholder="Телефон *" type="text">
                    </div>
                    <div class="mb-3">
                        <textarea name="mess" class="form-control" rows="2" maxlength="300" placeholder="Ваш запрос *" type="text"></textarea>
                    </div>
                    
                    <p>Можете приложить несколько фото высокого разрешения в разных ракурсах</p>
                    <div id="image-holder_2" class="d-flex flex-wrap"></div>
                    <div class="row">
                        <div class="col-12 col-sm-6">
                            <input class="file-upload d-none" type="file" name="files[]" value="" placeholder="Загрузить фото" multiple="true" accept="image" id="fileUpload_2" data-id="2" accepts=".jpg, .jpeg, .png, .webp">
                            <label for="fileUpload_2" class="download-file btn btn-transparent primary">Добавить фото</label>
                        </div>
                        <div class="col-12 col-sm-6 text-end">
                             <button type="submit" id="submit_2" autocomplete="off" class="btn btn-transparent btn-lg primary" disabled>Отправить заявку</button>
                        </div>
                    </div>
                   
                   <div class="mt-4">
                       <div class="form-check">
                          <input id="checkbox_2" class="form-check-input" type="checkbox" name="checkbox" onchange="document.getElementById('submit_2').disabled = !this.checked;">
                             <label for="checkbox_2" class="form-check-label small">Я соглашаюсь на обработку своих персональных данных в соответствии с <a href="/private/" class="link-border"> Политикой конфиденциальности</a></label>
                        </div>
                    </div>
        </form>
                
  </div>

JavaScript / jQuery . Размещаем в макете сайта во вкладке Javascript обработчик AJAX загрузки формы

/

// Функции без создания коллекции
$.extend({
    
  // Загрузка форм заявок на ajax с валидацией
    openForm: function(path, form_title) {
      $("#modalContent").load(path + " #formUpload", function(response){
        // Если ответ не пустой, открываем модальное окно
        if (response) {
            $('#applicationModal').modal('show').appendTo('body');
            $('#thema_2').text(form_title);
            $('#form2').validate({
  rules: {
    phone: "required",
    mess: "required"
  },
  messages: {
    mess: "Заполните это поле",
    phone: "Заполните это поле"
  },
  focusInvalid: true,
  errorClass: "input_error",
  submitHandler: function(form){
        var form = document.forms.form2,
        formData = new FormData(form),
        xhr = new XMLHttpRequest();
        xhr.open("POST", "/mform/send-zakaz.php");
        xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
          if(xhr.status == 200) {
            $("#form2").html('<p style="text-align:center">Ваша Заявка отправлена!<p>');
                setTimeout(function(){
                    location.reload();
                    }, 1000);
     
          }
        }
      };
      xhr.send(formData);
    }
    });
    

    $("#fileUpload_2").on('change', function () {
     //Get count of selected files
     var dataID = $(this).data('id');
     var countFiles = $(this)[0].files.length;
     var imgPath = $(this)[0].value;
     var extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase();
     var image_holder = $("#image-holder_" + dataID);
     console.log(dataID);
     image_holder.empty();
 
     if (extn == "png" || extn == "jpg" || extn == "jpeg" || extn == "webp") {
         if (typeof (FileReader) != "undefined") {
 
             //loop for each file selected for uploaded.
             for (var i = 0; i < countFiles; i++) {
 
                 var reader = new FileReader();
                 reader.onload = function (e) {
                     $("<img />", {
                         "src": e.target.result,
                             "class": "thumb-image"
                     }).appendTo(image_holder);
                 }
 
                 image_holder.show();
                 reader.readAsDataURL($(this)[0].files[i]);
             }
 
         } else {
             alert("Ваш брузер не поддерживает FileReader!");
         }
     } else {
         alert("Выберите файл в формате JPG, JPEG, PNG или WEBP!");
     }
    });
        }
    });
    },

............. остальной код

}

Информационный блок в карточке товара

Добавляем в карточку товара блок с призывом задать вопрос

1. В XSL шаблон вывода карточки товара в нужном месте размещаем данный код: 

<div class="ask-question">
            <div class="d-flex justify-content-center">
              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="40px" height="40px">
                <path style="fill:#ccc" d="M448 0H64C28.7 0 0 28.7 0 64v288c0 35.3 28.7 64 64 64h96v84c0 7.1 5.8 12 12 12 2.4 0 4.9-.7 7.1-2.4L304 416h144c35.3 0 64-28.7 64-64V64c0-35.3-28.7-64-64-64zm32 352c0 17.6-14.4 32-32 32H293.3l-8.5 6.4L192 460v-76H64c-17.6 0-32-14.4-32-32V64c0-17.6 14.4-32 32-32h384c17.6 0 32 14.4 32 32v288zm-224-88c-13.3 0-24 10.7-24 24s10.7 24 24 24 24-10.7 24-24-10.7-24-24-24zm-8.5-24h17c4.2 0 7.7-3.3 8-7.5l7-112c.3-4.6-3.4-8.5-8-8.5h-31c-4.6 0-8.3 3.9-8 8.5l7 112c.3 4.2 3.8 7.5 8 7.5z"></path>
              </svg>
              <span class="ps-3 fs-4 fw-bold title">Есть вопросы?</span>
            </div>
            <div class="text">
						Если у вас есть вопросы по товару, то задавайте их и мы вам с радостью ответим.
					</div>
            <div class="last">
              <button type="button" id="questionModalOpen" data-bs-toggle="modal" data-bs-target="#questionModal" class="btn btn-transparent">Задать вопрос о товаре</button>
            </div>
          </div>

2. В макет добавляем код загрузки формы (в примере код формы с отправкой уведомления Пользователю). Форма будет в модальном окне. Нажатие на кнопку "Задать вопрос" вызовет открытие формы.

<?php
        // ФОРМА ЗАЯВКИ
        $oForm = Core_Entity::factory('Form', 1);  
        $Form_Controller_Show = new Form_Controller_Show($oForm);
       if (!is_null(Core_Array::getPost($oForm->button_name)))
          {
         $Form_Controller_Show
         ->values($_POST + $_FILES)
         // 0 - html, 1- plain text
         ->mailType(0)
         ->mailXsl(
            Core_Entity::factory('Xsl')->getByName('ПисьмоКураторуФормыВФорматеHTML')
         )
         ->mailFromFieldName('euronasos19@gmail.com')
         ->process();

        // Отправляем письмо-подтверждение пользователю
        $Form_Controller_Show1 = clone $Form_Controller_Show;
        $sEmail = Core_Array::get($Form_Controller_Show1->values, 'email');  // здесь указать название поля input для email в форме
        if (Core_Valid::email($sEmail)){
            ob_start();
            $Form_Controller_Show1
                ->xsl(
                    Core_Entity::factory('Xsl')->getByName('ПисьмоПодтверждениеПользователюФормыВФорматеHTML')
                )
                ->show();
            $sMailText = ob_get_clean();
            if (mb_strpos($sMailText, 'ERROR TRUE') === FALSE){
                $subject = 'На сайте site.ru Вами была заполнена форма Заявки';// Тему письма отредактировать
                $oCore_Mail = Core_Mail::instance()
                    ->to($sEmail)
                    ->from('info@site.ru')
                    ->subject($subject)
                    ->message(trim($sMailText))
                    ->contentType('text/html')
                    ->header('X-HostCMS-Reason', 'Form');

                $oCore_Mail->send();
                      }
                  }
              }
                $Form_Controller_Show
                  ->xsl(
                  Core_Entity::factory('Xsl')->getByName('ОтобразитьФормуЗаявкиВМодальномОкне')
                 )
                  ->show();
?>

XSL . ФормаЗаявкиВМодальномОкне

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet SYSTEM "lang://351">
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:hostcms="http://www.hostcms.ru/"
exclude-result-prefixes="hostcms">
<xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml" />
<!-- ОтобразитьФорму -->
<xsl:template match="/">
<xsl:apply-templates select="/form" />
</xsl:template>
<xsl:template match="/form">
<xsl:choose>
<xsl:when test="success/node() and success = 1">
<!--div id="fade" style="display: block;"></div-->
<div class="modal fade form-success-result" id="modalSuccess" tabindex="-1" role="dialog">
<div class="modal-dialog  modal-dialog-centered">
<div class="modal-content p-5">
<div class="modal-header text-center">
<!--div class="modal-title w-100 fs-4 fw-bold">&labelModalTitle1;</div-->
<button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<xsl:value-of disable-output-escaping="yes" select="success_text" />
</div>
</div>
</div>
</div>
<script>
let modalSuccess = new bootstrap.Modal(document.getElementById('modalSuccess'));
modalSuccess.show();
</script>
</xsl:when>
<!-- Выводим ошибку (error), если она была передана через внешний параметр -->
<xsl:when test="error != ''">
<div class="modal fade form-success-result" id="modalError" tabindex="-1" role="dialog">
<div class="modal-dialog  modal-dialog-centered">
<div class="modal-content p-5">
<div class="modal-header text-center">
<div class="modal-title w-100 fs-3 fw-bold">ОШИБКА!</div>
<button class=" " type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body"><b><xsl:value-of disable-output-escaping="yes" select="error" /></b></div>
</div>
</div>
</div>
<script>
let modalError = new bootstrap.Modal(document.getElementById('modalError'));
modalError.show();
</script>
</xsl:when>
<xsl:when test="errorId/node()">
<div class="modal fade form-success-result" id="modalError" tabindex="-1" role="dialog">
<div class="modal-dialog  modal-dialog-centered">
<div class="modal-content p-5">
<div class="modal-header text-center">
<div class="modal-title w-100 fs-3 fw-bold">&labelERROR;</div>
<button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<xsl:choose>
<xsl:when test="errorId = 0">
&labelTextErrorId0;
</xsl:when>
<xsl:when test="errorId = 1">
&labelTextErrorId1;
</xsl:when>
<xsl:when test="errorId = 2">
&labelTextErrorId2;
</xsl:when>
</xsl:choose>
</div>
</div>
</div>
</div>
<script>
let modalError = new bootstrap.Modal(document.getElementById('modalError'));
modalError.show();
</script>
</xsl:when>
<xsl:otherwise>
 <div class="modal fade" id="questionModal" tabindex="-1" aria-labelledby="questionModalLabel_{@id}" aria-hidden="true">
<div class="modal-dialog  modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header justify-content-center">
<div class="h2 m-0 modal-title" id="questionModalLabel_{@id}"><xsl:value-of disable-output-escaping="yes" select="name" /></div>
<button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<xsl:if test="description !=''">
<xsl:value-of disable-output-escaping="yes" select="description" />
</xsl:if>
<div id="add-text" class="text-center"> </div>
<!-- Параметр action формы должен быть "./", если обработчик на этой же странице, либо "./form/", если обработчик на другой странице, например ./form/ -->
<form name="form{@id}" id="form{@id}" class="validate" action="./" method="post" enctype="multipart/form-data" >
<!-- Вывод разделов формы 0-го уровня -->
<xsl:apply-templates select="form_field_dir" />
<!-- Вывод списка полей формы 0-го уровня -->
<xsl:apply-templates select="form_field" />
<!-- Код подтверждения -->
<xsl:if test="captcha_id != 0">
<div class="form-group mb-3">
<div class="row">
<div class="col-6 caption">
<input type="hidden" name="captcha_id" value="{/form/captcha_id}"/>
<input type="text" name="captcha" size="15" class="form-control required" minlength="4" title="Введите число, которое указано на картинке."/>
<div class="padding-5">&labelCheckNumber1;<sup><font color="red">*</font></sup></div>
</div>
<div class="col-6 field">
<img id="formCaptcha_{/form/@id}_{/form/captcha_id}" src="/captcha.php?id={captcha_id}&amp;height=30&amp;width=100" class="captcha" name="captcha" />
<div class="captcha">
<img src="/images/refresh.png" /> <span class="small" onclick="$('#formCaptcha_{/form/@id}_{/form/captcha_id}').updateCaptcha('{/form/captcha_id}', 30); return false" style="cursor:pointer">&labelCheckNumber2;</span>
</div>
</div>
</div>
</div>
</xsl:if>
<!--div class="margin-bottom-10">
<div class="g-recaptcha" data-sitekey="6LfemXMiAAAAAPtn9tHaYj5z-0yKJq4Tmi3Ph1Cw"></div>
</div-->
<xsl:if test="csrf_token/node() and csrf_token != ''">
<input type="hidden" name="{csrf_field}" value="{csrf_token}" />
</xsl:if>
<div class="form-group">
<input type="hidden" name="{button_name}" value="submit"/>
<button id="submit_{@id}" value="submit" name="{button_name}" type="submit" class="btn btn-black mt-4 w-100"><xsl:value-of select="button_value" /></button>
</div>
</form>
<div class="mt-4">
<div class="form-check">
<input id="checkbox{@id}" class="form-check-input" type="checkbox" name="checkbox" checked="checked" onchange="document.getElementById('submit_{@id}').disabled = !this.checked;"/>
<label for="checkbox{@id}" class="form-check-label small" style="text-align:justify">Я соглашаюсь на обработку своих персональных данных в соответствии с <a href="/private" class="link-border"> Политикой конфиденциальности</a></label>
</div>
</div>
</div>
</div>
</div>
</div>
<SCRIPT>
<xsl:comment>
<xsl:text disable-output-escaping="yes">
    <![CDATA[
    $(function() {
    $("#fileUpload").on('change', function () {
 
     //Get count of selected files
     var countFiles = $(this)[0].files.length;
     var imgPath = $(this)[0].value;
     var extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase();
     var image_holder = $("#image-holder");
     image_holder.empty();
 
     if (extn == "png" || extn == "jpg" || extn == "jpeg" || extn == "webp") {
         if (typeof (FileReader) != "undefined") {
 
             //loop for each file selected for uploaded.
             for (var i = 0; i < countFiles; i++) {
 
                 var reader = new FileReader();
                 reader.onload = function (e) {
                     $("<img />", {
                         "src": e.target.result,
                             "class": "thumb-image"
                     }).appendTo(image_holder);
                 }
 
                 image_holder.show();
                 reader.readAsDataURL($(this)[0].files[i]);
             }
 
         } else {
             alert("Ваш брузер не поддерживает FileReader!");
         }
     } else {
         alert("Выберите файл в формате JPG, JPEG, PNG или WEBP!");
     }
    });
    });
]]>
</xsl:text>
</xsl:comment>
</SCRIPT>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="form_field_dir">
<!--fieldset class="maillist_fieldset"-->
<!--legend><xsl:value-of select="name" /></legend-->
<!-- Вывод списка полей формы -->
<xsl:apply-templates select="form_field" />
<!-- Вывод разделов формы -->
<xsl:apply-templates select="form_field_dir" />
<!--/fieldset-->
</xsl:template>
<xsl:template match="form_field">
<!-- Не скрытое поле и не надпись -->
<xsl:if test="type != 7 and type != 8">
<div class="form-group mb-3">
<xsl:if test="type = 2">
<xsl:attribute name="class">form-group mb-2 me-4</xsl:attribute>
</xsl:if>
<xsl:variable name="caption">
<xsl:choose>
<xsl:when test="obligatory = 1">
<xsl:value-of select="caption" />*
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="caption" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<!-- Радиокнопки -->
<xsl:when test="type = 3 or type = 9">
<xsl:apply-templates select="list/list_item" />
</xsl:when>
<!-- Checkbox -->
<xsl:when test="type = 4">
<div class="pretty p-default p-pulse">
<input type="checkbox" name="{name}">
<xsl:if test="checked = 1 or value = 1">
<xsl:attribute name="checked">checked</xsl:attribute>
</xsl:if>
</input>
<div class="state p-danger-o">
<label><xsl:value-of select="value" /></label>
</div>
</div>
</xsl:when>
<!-- Textarea -->
<xsl:when test="type = 5">
<textarea class="form-control" name="{name}" cols="{cols}" rows="{rows}" placeholder="{$caption}">
<xsl:if test="obligatory = 1">
<xsl:attribute name="class">form-control required</xsl:attribute>
</xsl:if>
<xsl:value-of select="value" />
</textarea>
</xsl:when>
<!-- Список -->
<xsl:when test="type = 6">
<select name="{name}" class="wide">
<xsl:if test="obligatory = 1">
<xsl:attribute name="class">wide required</xsl:attribute>
<xsl:attribute name="title"><xsl:value-of select="caption" /></xsl:attribute>
</xsl:if>
<option value="">...</option>
<xsl:apply-templates select="list/list_item" />
</select>
</xsl:when>
<!-- Текстовые поля -->
<xsl:otherwise>
<xsl:if test="type = 2">
<label for="fileUpload" class="download-file">
<i class="svg_icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="21px" height="21px" ><path style="fill:#797979" d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm16 336c0 8.822-7.178 16-16 16H48c-8.822 0-16-7.178-16-16V112c0-8.822 7.178-16 16-16h416c8.822 0 16 7.178 16 16v288zM112 232c30.928 0 56-25.072 56-56s-25.072-56-56-56-56 25.072-56 56 25.072 56 56 56zm0-80c13.234 0 24 10.766 24 24s-10.766 24-24 24-24-10.766-24-24 10.766-24 24-24zm207.029 23.029L224 270.059l-31.029-31.029c-9.373-9.373-24.569-9.373-33.941 0l-88 88A23.998 23.998 0 0 0 64 344v28c0 6.627 5.373 12 12 12h360c6.627 0 12-5.373 12-12v-92c0-6.365-2.529-12.47-7.029-16.971l-88-88c-9.373-9.372-24.569-9.372-33.942 0zM416 352H96v-4.686l80-80 48 48 112-112 80 80V352z"/></svg>
</i>
<span class="download-file-text ps-2">Выберите сразу несколько фото</span>
</label>
</xsl:if>
<input class="form-control" type="text" name="{name}" value="{value}" size="{size}" placeholder="{$caption}">
<xsl:choose>
<!-- Поле для ввода пароля -->
<xsl:when test="type = 1">
<xsl:attribute name="type">password</xsl:attribute>
</xsl:when>
<!-- Поле загрузки файла -->
<xsl:when test="type = 2">
<xsl:attribute name="type">file</xsl:attribute>
<xsl:attribute name="multiple">true</xsl:attribute>
<xsl:attribute name="class">photo</xsl:attribute>
<xsl:attribute name="name">files[]</xsl:attribute>
<xsl:attribute name="id">fileUpload</xsl:attribute>
<xsl:attribute name="accept">image/jpg,image/jpeg,image/png,image/webp</xsl:attribute>
<div id="image-holder" class="d-flex flex-wrap"></div>
</xsl:when>
<!-- HTML5: Дата -->
<xsl:when test="type = 10">
<xsl:attribute name="type">date</xsl:attribute>
</xsl:when>
<!-- HTML5: Цвет -->
<xsl:when test="type = 11">
<xsl:attribute name="type">color</xsl:attribute>
</xsl:when>
<!-- HTML5: Месяц -->
<xsl:when test="type = 12">
<xsl:attribute name="type">month</xsl:attribute>
</xsl:when>
<!-- HTML5: Неделя -->
<xsl:when test="type = 13">
<xsl:attribute name="type">week</xsl:attribute>
</xsl:when>
<!-- HTML5: Время -->
<xsl:when test="type = 14">
<xsl:attribute name="type">time</xsl:attribute>
</xsl:when>
<!-- HTML5: Дата-Время -->
<xsl:when test="type = 15">
<xsl:attribute name="type">datetime</xsl:attribute>
</xsl:when>
<!-- HTML5: E-mail -->
<xsl:when test="type = 16">
<xsl:attribute name="type">email</xsl:attribute>
</xsl:when>
<!-- HTML5: Поиск -->
<xsl:when test="type = 17">
<xsl:attribute name="type">search</xsl:attribute>
</xsl:when>
<!-- HTML5: Телефон -->
<xsl:when test="type = 18">
<xsl:attribute name="type">tel</xsl:attribute>
</xsl:when>
<!-- HTML5: URL -->
<xsl:when test="type = 19">
<xsl:attribute name="type">url</xsl:attribute>
</xsl:when>
<!-- Текстовое поле -->
<xsl:otherwise>
<xsl:attribute name="type">text</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="obligatory = 1">
<xsl:attribute name="class">form-control required</xsl:attribute>
</xsl:if>
<!--xsl:if test="@id = 15">
<xsl:attribute name="disabled">disabled</xsl:attribute>
</xsl:if-->
</input>
</xsl:otherwise>
</xsl:choose>
</div>
</xsl:if>
<!-- скрытое поле -->
<xsl:if test="type = 7">
<input type="hidden" name="{name}" value="{value}" />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
//
//
//
Код DTD для языка "ru"

<!ENTITY labelModalTitle1 "Благодарим вас за заявку!">
<!ENTITY labelModalTitle2 "Заявка получена.">
<!ENTITY labelSuccessText "Мы свяжемся с вами в ближайшее время">
<!ENTITY labelSuccessText2 "Наш менеджер ответит на все ваши вопросы">

<!ENTITY labelERROR "ОШИБКА!">
<!ENTITY labelTextErrorId0 "Вы неправильно ввели код подтверждения отправки формы!">
<!ENTITY labelTextErrorId1 "Заполните все обязательные поля!">
<!ENTITY labelTextErrorId2 "С момента отправки последней формы прошло слишком мало времени!">

<!ENTITY labelTextError1 "Это поле пустое">
<!ENTITY labelTextError2 "E-mail должен иметь вид name@domain.com">

<!ENTITY labelText1 "Обязательное поле">

<!ENTITY labelCheckNumber1 "Проверочный код">
<!ENTITY labelCheckNumber2 "показать другой код">

Стили CSS

/******************************/

.ask-question {
    padding:20px;
    background-color: #f0f0f0;
    max-width:450px;
    text-align: center;
}
.ask-question > div:not(.last) {
     margin-bottom:15px;   
}

.btn.btn-transparent {

background: transparent;

border: 1px solid #212529;

color: #212529;

/******************************/

Использование jQuery Validate для проверки форм

Рабочая валидация с проверкой captcha
<script>
      $(function() {
            $(".validate").validate({
            focusInvalid: true,
            errorClass: "input_error",
            onkeyup: false,
            onfocusout: false,
            rules: {
            captcha: {
            required: true,
            remote: '/forms/'
            },
            author: "required",
             text: "required",
             email: {
                   required: true,
                   email: true
                  }
               },
               messages: {
                       author: "Пожалуйста, укажите своё Имя!",
                       text: "Пожалуйста, напишите свой комментарий!",
                       captcha: "Вы не вписали контрольное число!",
                       email: {
                       required: "Введите свой email",
                       email: "E-mail должен быть в формате name@domain.com"
                         }
            }});
        });
   </script>
Проверьте, подключен ли в макете файл jquery.validate.min.js, в системе управления он расположен в hostcmsfiles/jquery/jquery.validate.min.js
Core_Page::instance()
// jQuery
->js('/hostcmsfiles/jquery/jquery.min.js')
...
// Validate
->js('/hostcmsfiles/jquery/jquery.validate.min.js')
...
->showJs();

Указание валидации формы

Валидация конкретной формы или форм с классом validate указывается следующим образом:
<script>
$(".validate").validate();
</script>
Валидация всех форм:

<script>
$("form").validate();
</script>

Способы валидации форм

Использование имена классов как правила

Тем полям, которые нужно проверять, добавляете атрибут class="required"
<form action="." method="post" class="validate">
    <input type="text" name="name" value="" class="required" />
    <input type="submit" value="Submit" />
</form>

С помощью метод addClassRules вы можете расширить и добавить условие валидации:
<form action="." method="post" class="validate">
    <input type="text" name="name" value="" class="name" />
    <input type="text" name="zip" value="" class="zip" />
    <input type="submit" value="Submit" />
</form>
Проверка скриптом
<script >
$.validator.addClassRules("name", {
    required: true,
    minlength: 2
});
</script >
или сразу для нескольких полей:
<script >
$.validator.addClassRules({
name: {
required: true,
minlength: 2
},
zip: {
required: true,
digits: true,
minlength: 5,
maxlength: 5
}});
</script >

Передача опций при инициализации validate()

Методу validate() передаем объекты rules и messages которые состоят из пар ключ/значение. В rules в качестве ключа указываем атрибут name поля, значением указываем правило проверки. В messages в качестве ключа так же идет атрибут name поля, а в качестве значения указываем предупреждающее сообщение.
<script >
$(".validate").validate({
rules: {
name: "required",
email: {
required: true,
email: true
}
},
messages: {
name: "Please specify your name",
email: {
required: "Введите свой email",
email: "E-mail должен быть в формате name@domain.com"
}
}});
</script >
Список правил:
  • required — поле обязательное для заполнения (true или false);
  • remote — указывается файл для проверки поля (например: "check.php");
  • email — проверяет корректность e-mail адреса (true или false);
  • url — проверяет корректность url адреса (true или false);
  • date — проверка корректности даты (true или false);
  • dateISO — проверка корректности даты ISO (true или false);
  • number — проверка на число (true или false);
  • digits — только цифры (true или false);
  • creditcard — корректность номера кредитной карты (true или false);
  • equalTo — равное чему-то (например другому полю equalTo: "#pswd");
  • accept — проверка на правильное расширение (accept: "xls|csv");
  • maxlength — максимальное кол-во символов;
  • minlength — минимальное кол-во символов;
  • rangelength — кол-во символов от скольких и до скольких (rangelength: [2, 5]);
  • range — число должно быть в диапазоне от и до (range: [2, 12]);
  • max — максимальное значение числа;
  • min — минимальное значение числа.

Пример использования в XSL-шаблоне

Указываем форме класс например validate, и далее в XSL-шаблоне формы добавляем код проверки формы (пример взят из формы быстрого заказа в адаптивном шаблоне):
<script >
$(".validate").validate({
rules: {
surname: "required",
name: "required",
email: {
required: true,
email: true
}
},
messages: {
surname: "Введите фамилию!",
name: "Введите имя!",
email: {
required: "Введите e-mail!",
email: "Адрес должен быть вида name@domain.com"
}
},
focusInvalid: true,
errorClass: "input_error"
});
</script>
Проверка валидации и заблокированного E-mail нежелательного Пользователя
<SCRIPT>
            $(document).ready(function() {
            $('.select2').addClass('required');
            $("#form<xsl:value-of select="@id" />").submit(function (event) {
            event.preventDefault();
            var eml = $('input[name=email]').val();
            if(eml =='svinya.svintus@yandex.ru') {
            alert ('Вы не можете отправлять нам письма');
            $(location).attr('href', '/');
            }else{
            
            $(this).validate({
            focusInvalid: true,
            errorClass: "input_error"
            });
            }
            });
            });
        </SCRIPT>

Модальное окно с формой подписки и сохранением куки на jQuery

Добавляем в макет код показа формы:

<?php
        // add ФОРМА ПОДПИСКА ЗА СКИДКУ
     $oForm = Core_Entity::factory('Form', 49);  // заменить ID формы
      
     $Form_Controller_Show = new Form_Controller_Show($oForm);
      
     if (!is_null(Core_Array::getPost($oForm->button_name)))
     {
     $Form_Controller_Show
     ->values($_POST + $_FILES)
     // 0 - html, 1- plain text
     ->mailType(0)
     ->mailXsl(
     Core_Entity::factory('Xsl')->getByName('ПисьмоКураторуФормыВФорматеHTML')
     )
     ->mailFromFieldName('email')
     ->process();
     }
      
     $Form_Controller_Show
     ->xsl(
     Core_Entity::factory('Xsl')->getByName('ОтобразитьФормуВМодальномОкне')
     )
      ->show();
?>

Подключаем в макете скрипт jquery.cookie.min.js:

<script src="/js/jquery.cookie.min.js"></script>

Проверяем куки:

<script>
$(document).ready(function($) {
if ($.cookie('was') == null) {
// Покажем всплывающее окно
setTimeout(function(){$('#popupModal').modal('show');}, 36000);
}
// Запомним в куках, что посетитель к нам уже заходил
$.cookie('was', 'value', { expires: 2, path: '/' });
});
</script>

JavaScript / jQuery . jquery.cookie.min.js

jQuery.cookie=function(b,j,m){if(typeof j!="undefined"){m=m||{};if(j===null){j="";m.expires=-1}var e="";if(m.expires&&(typeof m.expires=="number"||m.expires.toUTCString)){var f;if(typeof m.expires=="number"){f=new Date();f.setTime(f.getTime()+(m.expires*24*60*60*1000))}else{f=m.expires}e="; expires="+f.toUTCString()}var l=m.path?"; path="+(m.path):"";var g=m.domain?"; domain="+(m.domain):"";var a=m.secure?"; secure":"";document.cookie=[b,"=",encodeURIComponent(j),e,l,g,a].join("")}else{var d=null;if(document.cookie&&document.cookie!=""){var k=document.cookie.split(";");for(var h=0;h<k.length;h++){var c=jQuery.trim(k[h]);if(c.substring(0,b.length+1)==(b+"=")){d=decodeURIComponent(c.substring(b.length+1));break}}}return d}};

Отправка формы в новую вкладку браузера

Отправка формы в новую вкладку браузера
Содержание
  1. 1. Отдельная кнопка
  2. 2. Скрытая форма

Иногда возникает вопрос – как отправить HTML-форму в новую вкладку браузера, например для отдельной страницы, где будет печать введённых данных? Как и у ссылки, у элемента можно применить атрибут target(валидный только в HTML-5), который устанавливает поведение окна, при отправки формы. Возможные значения:

_blank
Загружает страницу в новое окно браузера
_self
Загружает страницу в текущее окно (значение по умолчанию)
_parent
Загружает страницу во фрейм-родитель, если фреймов нет, то это значение работает как _self
_top
Отменяет все фреймы и загружает страницу в полном окне браузера, если фреймов нет, то это значение работает как _self.

Пример:

<form action="" method="post" target="_blank">
	<input type="text" name="name">
	<input type="text" name="surname">		
	<button type="submit" name="print">Распечатать</button>
	<button type="submit" name="send">Далее</button>
</form>

Минусом является то, что при нажатии любой кнопки type="submit"форма отправится в новую вкладку браузера.

1. Отдельная кнопка

Если требуется разделить события кнопок, то можно добавлять атрибут target="_blank"только при нажатии на кнопку «Распечатать» и после отправки формы его удалять с помощью JQuery.

<form action="" method="post">
	<input type="text" name="name">
	<input type="text" name="surname">		
	<button type="button" name="print">Распечатать</button>
	<button type="submit" name="send">Далее</button>
</form>	
<script>
$(document).ready(function(){
	$("input[name='print']").click(function(){
		var form =  $(this).closest('form');
		form.attr('target', '_blank');
		form.submit();
		form.attr('target', '');
	});
	return false;
});
</script>

2. Скрытая форма

Следующий вариант поможет, когда нужно отправить форму в новую вкладку и на другой URL. В данном примере все поля основной формы копируются в скрытую.

<form style="display: none;" action="/new.php?print=1" target="_blank" method="post" id="form-print"></form>	
<form action="" method="post" id="form-original">
	<input type="text" name="name">
	<input type="text" name="surname">		
	<button type="button" name="print">Распечатать</button>
	<button type="submit" name="send">Далее</button>
</form>	
<script>
$(document).ready(function(){
	$("input[name='print']").click(function(){
		$('#form-print').html($('#form-original').html());
		$('#form-print').submit();
	});
	return false;
});
</script>

Сохраняем в сессии введённые Пользователем данные и подставляем их в поля при перезагрузке страницы

Сохраняем в сессии (sessionStorage) значения, введённые в поля формы, для сохранения их в случае перезагрузки страницы.

JavaScript
    // выбираем на странице все элементы типа textarea и input
    document.querySelectorAll('textarea, input').forEach(function(e) {
        // если данные значения уже записаны в sessionStorage, то вставляем их в поля формы
        // путём этого мы как раз берём данные из памяти браузера, если страница была случайно перезагружена
        if(e.value === '') e.value = window.sessionStorage.getItem(e.name, e.value);
        // на событие ввода данных (включая вставку с помощью мыши) вешаем обработчик
        e.addEventListener('input', function() {
            // и записываем в sessionStorage данные, в качестве имени используя атрибут name поля элемента ввода
            window.sessionStorage.setItem(e.name, e.value);
        });
    });

Строка поиска <input type="number"> name имя ключа параметра value значение ключа

Строка поиска <input type="number">

 name
имя ключа параметра
 value
значение ключа параметра, которое может содержать: цифры «0-9», один плюс «+» или минус «-», один символ «e» или «E», одну точку «.». Можно ввести буквы, но форма не будет отправлена при нажатии на кнопку submit, а покажет сообщение об ошибке. Чаще всего не задаётся. Пользователь может его изменить, если не указаны атрибуты readonly и disabled.
 readonly
заблокировано изменение пользователем
 disabled
заблокированы доступ, изменение пользователем и передача данных текущего параметра
 required
поле не может быть пустым
 step
 step=20
 step=any
шаг изменения, который может быть положительным целым или дробным числом. Значение value кратно значению step, то есть делится на него без остатка. Пример разрешённых значений value при step="20": …, -40, -20, 0, 20, 40, …. По умолчанию равен 1. То есть <input type="number"/> покажет ошибку, если ввести десятичную дробь. Для того, чтобы убрать ограничения, нужно присвоить any.
 min
минимально возможное значение value, необходимое для отправки формы
 max
максимально возможное значение value, необходимое для отправки формы
 placeholder
подсказка-заглушка
 title
всплывающая подсказка при наведении курсора мышки
 autocomplete
автозаполнение. Можно его отключить или сделать более конкретизированным.
 list
список рекомендованных значений
 autofocus
фокус поля (то есть период между щелчком по элементу и щелчком вне элемента) получен при загрузке документа
<form>
<input type="number">
<input type="submit">
</form>
Поле <input type="number"> не подходит для текстовых строк, состоящих из 16 и более цифр, например, номера пластиковой карты, так как длинные числа от 9007199254740991 могут округляться.

Количество товара

  1. увеличение и уменьшение значения числового поля с помощью кнопок пошагового изменения;

  2. сообщение об ошибке при вводе букв и дробных чисел;

  3. минимальное значение 1 шт.

шт
<form><input type="number" min="1" value="1"> шт</form>

Чётные положительные целые числа

<form><input type="number" step="2" min="2"></form>

Нечётные положительные целые числа

<form><input type="number" step="2" min="1"></form>

Круглые числа

<form><input type="number" step="10"></form>

Десятичная дробь с плавающей запятой

<form><input type="number" step="any"></form>

Денежный формат цены: «рубли,копейки» с двумя знаками после запятой ₽

<form><input type="number" step="0.01" min="0" placeholder="0,00"> ₽</form>

Ограничить количество символов в поле до 5

<form><input type="number" min="-9999" max="99999"></form>

Уменьшить длину <input type=number>

Атрибуты minlength, maxlength  и size не работают.

Необходимое количество символов в поле определяется атрибутами min, max и step: 

<!-- Достаточно выделить место на 6 символов -->
<input type="number" min="0" max="100" step="0.01"/>

Указать ширину в CSS (свойство width):

<style>
#dva {
width: 6em;
}
</style>
<input type="number" min="0" max="100" step="any" id="dva"/>

Стрелки у <input type=number>

Чтобы стрелочки были изначально, а не только при :hover и :focus в Chrome

<style>
#pyat::-webkit-inner-spin-button,
#pyat::-webkit-outer-spin-button  {
opacity: 1;
}
</style>
<input type="number" id="pyat"/>

Убрать стрелки

<style>
.raz {
-moz-appearance: textfield;
}
.raz::-webkit-inner-spin-button {
display: none;
}
</style>

<input type="number" class="raz"/>

Стилизация стрелок «вверх»/«вниз» CSS

<style>
#raz {
position: relative;
display: inline-block;
}
#raz input {
font-size: 1em;
-moz-appearance: textfield;
}
#raz input::-webkit-inner-spin-button {
display: none;
}
#raz span {
position: absolute;
}
@supports (clip-path: polygon(50% 30%, 10% 90%, 90% 90%)) {
#raz input {
padding-right: 1em;
}
#raz span {
right: 0;
width: 1em;
height: 50%;
background: rgb(70,70,70);
cursor: pointer;
}
#raz span:hover {
background: red;
}
#raz span:nth-of-type(1) {
top: 0;
clip-path: polygon(50% 30%, 10% 90%, 90% 90%);
}
#raz span:nth-of-type(2) {
bottom: 0;
clip-path: polygon(50% 70%, 10% 10%, 90% 10%);
}
}
</style>
<span id="raz">
<input type="number" value="0">
<span onclick="this.previousElementSibling.stepUp()"></span>
<span onclick="this.previousElementSibling.previousElementSibling.stepDown()"></span>
</span>

Запретить ввод в поле, чтобы можно было пользоваться только кнопками редактирования

 <style>
.raz {
all: unset;
-moz-appearance: textfield;
width: 3em;
text-align: center;
}
.raz::-webkit-inner-spin-button {
display: none;
}
</style>

<button type="button" onclick="this.nextElementSibling.stepDown()">-</button>
<input type="number" min="0" max="100" value="1" readonly class="raz">
<button type="button" onclick="this.previousElementSibling.stepUp()">+</button>

Вместо <input type="number"> использовать <input type="text">

Виртуальная клавиатура с дробно-числовым вводом

Атрибут inputmode  плохо поддерживается браузерами.

<input inputmode="decimal">

На IOS (iPhone) если атрибут patternимеет значение [0-9]* или \d*, то на мобильном телефоне будет дана цифровая клавиатура.

Для отправки формы, поле должно содержать только цифры

<form><input inputmode="decimal" pattern="-?(\d+|\d+.\d+|.\d+)([eE][-+]?\d+)?"></form>

Функционал с JavaScript

При наборе не числа value возвратит пустое значение. Браузер может позволить/вынудить пользователя набрать число с запятой «20,5», но value примет значение с точкой «20.5», искл., IE.

value (строка) =""; valueAsNumber (число) ="";

 value (строка) =""; valueAsNumber (число) ="";

Запретить ввод букв

<input type="number" id="check" step="any"/>
<script>
document.getElementById('check').onkeydown = function (e) {
return !(/^[А-Яа-яA-Za-z ]$/.test(e.key)); // IE > 9
}
</script>

Только одна точка или одна запятая

<input type="number" step="any" id="shest2"/>

<script>
document.getElementById('shest2').onkeydown = function (e) {
if (e.currentTarget.value.indexOf(".") != '-1' || e.currentTarget.value.indexOf(",") != '-1') {
return !(/[.,]/.test(e.key));
}
}
</script>

Ограничить ввод двумя знаками после запятой

Цифры, вносимые после 2-го символа после запятой, сразу обрезаются, дабы не совершать лишних телодвижений.

<input type="number" oninput="up(this)" step="0.01"/>

<script>
function up(e) {
if (e.value.indexOf(".") != '-1') {
e.value=e.value.substring(0, e.value.indexOf(".") + 3);
}
}
</script>

Обрезать последний лишний символ

<input type="number" step="any" id="shest1"/>

<script>
document.getElementById('shest1').oninput = function () {
if (this.value.length > 7) this.value = this.value.substr(0, 7); // в поле можно ввести только 7 символов
}
</script>

Разделение цифр пробелом

Наберите любое многозначное число

<input oninput="this.nextElementSibling.innerHTML = new Intl.NumberFormat('ru').format(this.valueAsNumber)" type="number">
<output> </output>

ФОРМА "ЗАКАЗАТЬ ЗВОНОК" В МОДАЛЬНОМ ОКНЕ НА BOOTSTRAP

Вызов формы
<button type="button" class="button" data-bs-toggle="modal" data-bs-target="#basicModal" >
         <i class="fa fa-phone"></i> <span>Заказать звонок</span>
</button>

Добавить в Макет

<div class="modal fade" id="basicModal" tabindex="-1" role="dialog">
     <div class="modal-dialog">
       <div class="modal-content">
          <div class="modal-header"><button class="close" type="button" data-dismiss="modal">x</button> </div>
        <div class="modal-body">

      <?php
        // add ФОРМА ЗАКАЗАТЬ ЗВОНОК
        $oForm = Core_Entity::factory('Form', 5);  // Заменить id формы
        $Form_Controller_Show = new Form_Controller_Show($oForm);
       if (!is_null(Core_Array::getPost($oForm->button_name)))
          {
         $Form_Controller_Show
         ->values($_POST + $_FILES)
         // 0 - html, 1- plain text
         ->mailType(1)
         ->mailXsl(
            Core_Entity::factory('Xsl')->getByName('ПисьмоКураторуФормы')
         )
         ->mailFromFieldName('mail@spartika.ru')
         ->process();

        // Отправляем письмо-подтверждение пользователю
        $Form_Controller_Show1 = clone $Form_Controller_Show;
        $sEmail = Core_Array::get($Form_Controller_Show1->values, 'email');
        if (Core_Valid::email($sEmail)){
            ob_start();
            $Form_Controller_Show1
                ->xsl(
                    Core_Entity::factory('Xsl')->getByName($Form_Controller_Show1->mailType == 0 ? 'ПисьмоПодтверждениеПользователюФормыВФорматеHTML' : 'ПисьмоПодтверждениеПользователюФормы')
                )
                ->show();
            $sMailText = ob_get_clean();
            if (mb_strpos($sMailText, 'ERROR TRUE') === FALSE){
                $subject = 'Вами была заполнена форма обратного звока на сайте  jvern.club';// Тема письма отредактировать
                // При текстовой отправке нужно преобразовать HTML-сущности в символы
                $Form_Controller_Show->mailType == 1 && $sMailText = html_entity_decode(strip_tags($sMailText), ENT_COMPAT, 'UTF-8');
                $oCore_Mail = Core_Mail::instance()
                    ->to($sEmail)
                    ->from(EMAIL_TO)
                    ->subject($subject)
                    ->message(trim($sMailText))
                    ->contentType($Form_Controller_Show->mailType == 0 ? 'text/html' : 'text/plain')
                    ->header('X-HostCMS-Reason', 'Form');
                // Массив содержащий пути прикрепленных файлов и их имена
                $aForm_Fields = $oForm->Form_Fields->findAll();
                foreach ($aForm_Fields as $oForm_Field){
                    if ($oForm_Field->type == 2){// File
                        $value = Core_Array::get($Form_Controller_Show->values, $oForm_Field->name);
                        if (!is_null($value)){
                            if (is_array($value) && $value['size'] > 0){
                                $oForm_Fill_Fields = Core_Entity::factory('Form_Fill_Field');
                                $oForm_Fill_Fields->queryBuilder()
                                    ->where('form_field_id', '=', $oForm_Field->id)
                                    ->where('value', '=', $value['name'])
                                    ->limit(1);
                                $aForm_Fill_Fields = $oForm_Fill_Fields->findAll();
                                if (isset($aForm_Fill_Fields[0])){
                                    $oCore_Mail->attach(array(
                                        'filepath' => $aForm_Fill_Fields[0]->getPath(),
                                        'filename' => $value['name']
                                    ));
                                }
                            }
                        }
                    }
                }

                $oCore_Mail->send();
            }
        }
        // END add
              }
                $Form_Controller_Show
                  ->xsl(
                  Core_Entity::factory('Xsl')->getByName('ОтобразитьФорму')
                 )
                  ->show();
              ?>
        </div>
    </div>
  </div>
</div>

XSL

// XSL-шаблон "ПисьмоПодтверждениеПользователюФормы"
<xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:hostcms="http://www.hostcms.ru/"
exclude-result-prefixes="hostcms">
<xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="form/success/node() and form/success = 1"><xsl:apply-templates select="/form"/></xsl:when>
<xsl:otherwise>ERROR TRUE</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match='/form'>
<p><b>Здравствуйте, <span><xsl:value-of disable-output-escaping="yes" select="form_field[@id=1]/value" /></span>!</b></p>
<p>Вы заполнили Форму на сайте egeriya.ru</p>
<!-- Вывод разделов формы 0-го уровня -->
<xsl:apply-templates select="form_field_dir[.//form_field[value != ''][1]]" />
<!-- Вывод списка полей формы 0-го уровня -->
<xsl:apply-templates select="form_field[value != '']" />
<p>В ближайшее время с Вами свяжется наш менеджер.<br />
Благодарим за доверие!</p>
<p>Команда интернет-магазина "Эгерия"</p>
<p><a href="http://egeriya.ru">egeriya.ru</a></p>
<xsl:value-of select="nameg"/>
</xsl:template>
<xsl:template match="form_field_dir">
- <xsl:value-of select="name" />
<!-- Вывод списка полей формы -->
<xsl:apply-templates select="form_field[value != '']" />
<!-- Вывод разделов формы -->
<xsl:apply-templates select="form_field_dir[.//form_field[value != ''][1]]" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="form_field">
<xsl:value-of select="caption"/>:<xsl:text> </xsl:text><xsl:choose><xsl:when test="values/node()"><xsl:apply-templates select="values/value"/></xsl:when><xsl:otherwise><xsl:value-of select="value" /></xsl:otherwise></xsl:choose><xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="values/value"><xsl:variable name="currentValue" select="." /><xsl:value-of select="../../list/list_item[@id=$currentValue]/value"/><xsl:if test="position() != last()"><xsl:text>, </xsl:text></xsl:if></xsl:template>
</xsl:stylesheet>
//XSL-шаблон "ПисьмоПодтверждениеПользователюФормыВФорматеHTML"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:hostcms="http://www.hostcms.ru/"
exclude-result-prefixes="hostcms">
<xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="form/success/node() and form/success = 1"><xsl:apply-templates select="/form"/></xsl:when>
<xsl:otherwise>ERROR TRUE</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/form">
<p><b><xsl:value-of disable-output-escaping="yes" select="name"/></b></p>
<p>Вы заполнили Форму на сайте egeriya.ru</p>
<!-- Вывод разделов формы 0-го уровня -->
<xsl:apply-templates select="form_field_dir[.//form_field[value != ''][1]]" />
<!-- Вывод списка полей формы 0-го уровня -->
<xsl:apply-templates select="form_field[value != '']" />
</xsl:template>
<xsl:template match="form_field_dir">
<p><b><xsl:value-of select="name" /></b></p>
<!-- Вывод списка полей формы -->
<xsl:apply-templates select="form_field[value != '']" />
<!-- Вывод разделов формы -->
<xsl:apply-templates select="form_field_dir[.//form_field[value != ''][1]]" />
</xsl:template>
<xsl:template match="form_field">
<p>
<b>
<xsl:value-of select="caption" disable-output-escaping="yes"/>
</b>:
<xsl:choose>
<xsl:when test="values/node()">
<xsl:apply-templates select="values/value"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="value" />
</xsl:otherwise>
</xsl:choose>
</p>
</xsl:template>
<xsl:template match="values/value"><xsl:variable name="currentValue" select="." /><xsl:value-of select="../../list/list_item[@id=$currentValue]/value"/><xsl:if test="position() != last()"><xsl:text>, </xsl:text></xsl:if></xsl:template>
</xsl:stylesheet>

ФОРМА "КУПИТЬ В 1 КЛИК" на BOOTSTRAPE

Кнопка вызова формы - добавляем в карточку товара или в каталог товаров
HTML
<button type="button" class="button btn-primary send-zakaz" data-item="{name}" data-bs-toggle="modal" data-bs-target="#zakazModal">
<i class="fa fa-shopping-cart"></i>
<span>Купить</span>
</button >
HTML. Код формы
<div class="modal fade" id="zakazModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
    <h4 class="modal-title" id="myModalLabel">Купить в 1 клик</h4>
    <button class="btn-close" type="button" data-bs-dismiss="modal"></button>
</div>
<div class="box-content">
<form method="get" class="form_call1">
<div class="row">
    <div class="col-xs-12 text">
        <h6>Модель: <span id="item"></span></h6>
    </div>
    <div class="col-xs-12 text-center mess" id="form_err1"></div>
    <div class="col-xs-12 text-center mess" id="form_right1"></div>
    <div class="col-xs-12">
        <input id="name" maxlength="80" placeholder="Ваше Имя" type="text" />
    </div>
    <div class="col-xs-12">
        <input id="phone" maxlength="80" placeholder="Телефон *" type="text" required/>
    </div>
    <div class="col-xs-12">
        <button type="submit" autocomplete="off" class="zakaz-submit">Отправить заказ</button>
    </div>
</div>
</form>
</div>
</div>
</div>
</div>
Валидация формы на jQuery
$(function() {
     $('form.form_call1').submit(function(){
     phone=$('input#phone').val();
     name=$('input#name').val();
         reg = /^\d[\d\(\)\ -]{5,14}\d$/;
         item=$('#item').text();
     err='';
          if (phone =='') {err+='<span>Вы не указали свой телефон</span>';}
      else  if (!(reg.test(phone))) {err+='  <span>Неверно указан Телефон</span>';}
      if (err!='') {
       $('div#form_err').html(err);  // показываем ошибку
          } else {
       $.get( "/forms/send-zakaz.php?phone="+phone+"&name="+name+"&item="+item, function( data ) {  //указываем путь к обработчику отправки формы 
       $('div#form_right').html(data);  // показываем подтверждение удачной отправки
           setTimeout(function(){
                    location.reload();
                    }, 1000);
           });
           }
             return false;
       });

      $('.send-zakaz').click(function() {
            var nameg = $(this).data('item');
            // Добавляем в id=item название товара
            $('span#item').html(nameg);
            // Добавляем в дополнительное поле input название товара
            $('input[name=item]').val(nameg);
      });
});

PHP . Обработчик отправки фоормы

<?php
//несколько ящиков перечисляются через запятую
$to = "zakaz@site.ru" ;
if( $_GET['phone'] ) {
$subject = "Заказ на сайте SITE.RU";
$message = '
<p>Пользователь сайта SITE.RU заполнил форму Заказа в 1 клик:<br/>
<strong>Имя: </strong>'.$_GET['name'].'<br/>
<strong>Телефон: </strong>'.$_GET['phone'].'<br/>
<strong>Товар: </strong>'.$_GET['item'].'
</p>
';
$headers = "Content-type: text/html; charset=utf-8 \r\n";
$headers .= "From: zakaz@site.ru\r\n";
mail($to, $subject, $message, $headers);
//header('Content-Type: application/json');
}
?>
<span>Заказ успешно отправлен!</span>

Стили CSS

.modal-content {border-radius: 13px; box-shadow: 0 0 12px rgba(0,0,0,.2); position: relative!important; width:100%; max-width: 600px!important; background: #fff!important; border: 3px solid rgb(251,119,3)!important; padding: 20px; margin: 3% auto!important;}
.modal-header {padding: 0 0 10px;}
.modal-header .close { background: url(/images/close.png) no-repeat; display:inline-block; padding: 25px 25px 0 0!important; opacity:.7; }
.modal-header h4 {padding: 20px 55px 0; font-size: 32px!important; font-weight:300;}
.modal-content h6 {font-size: 18px!important; font-weight: 600!important; color:#666; margin: 19px 0 35px!important;}
.modal-content .box-content {padding: 0 55px;}
.modal-content input {
float: left!important;
height: 40px!important;
color: #b4b4b4!important;
font-size: 16px!important;
border: 2px solid #b4b4b4!important;
border-radius: 3px!important;
padding: 0 7px!important;
}
.modal-content button[type="submit"] {
float: left!important;
height: 44px;
position: inherit!important;
background: url(/images/button_icon.png) 0 50% no-repeat;
background-color: rgb(251,119,3)!important;
color: #fff!important;
font-size: 18px!important;
font-weight: 100!important;
border: none!important;
cursor: pointer!important;
padding: 0 17px 0 46px!important;
}
.modal-content input, .modal-content button[type="submit"] {margin: 20px 0; width:100%;}

Форма с валидацией (в модальном окне)

Для валидации формы используем скрипт validate.js

$(function() {

var errorTxt = 'Ошибка отправки формы'; //Ошибка отправки формы
$('#form1').validate({
  rules: {
    name: "required",
    phone: "required"
  },
  messages: {
    name: "Заполните это поле", // Заполните это поле
    phone: "Заполните это поле",
  },
  focusInvalid: true,
  errorClass: "input_error",
  submitHandler: function(form){
        var form = document.forms.form1,
        formData = new FormData(form),
        xhr = new XMLHttpRequest();
        xhr.open("POST", "/mform/send-zakazspec.php");
        xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
          if(xhr.status == 200) {
            $("#form1").html('<p style="text-align:center">Ваша Заявка отправлена!<p>');
                setTimeout(function(){
                    location.reload();
                    }, 1000);
     
          }
        }
      };
      xhr.send(formData);
    }
    });

     //  Получаем значение из span.title и добавляем в поле формы название выбранной спецтехники
      $('.predzakaz').click(function() {
            var nameg = $(this).parent().children('span.title').text();
            $('#predzakazModal').appendTo('body');//перемещаем по DOM в конец body
            $('input#model').val(nameg);
            $('#model-name').html(nameg);
      });
});

HTML код формы

<div class="modal fade" id="predzakazModal" tabindex="-1" role="dialog">
     <div class="modal-dialog">
          <div class="modal-content">
                         <div class="modal-header text-center"><button class="close" type="button" data-dismiss="modal"></button>
                                   <div class="modal-title h3">РемонтССТ </div>
                                    <p class="bold"> Узнать стоимость:  <span id="model-name"></span></p>
                         </div>
                        <div class="box-content">
                                 <form method="get" id="form1">
                                             <div class="text-center mess" id="form_err1"></div>
                                             <div class="text-center mess"  id="form_right1"></div>
                                             <input  name="model"  id="model" type="hidden" />
                                             <input name="name" id="name" maxlength="80" placeholder="Ваше Имя" type="text" />
                                             <input name="phone" id="phone" maxlength="80" placeholder="Телефон *" type="text" required/>


                                            <button type="submit" autocomplete="off" class="btn btn-primary btn-lg btn-circle">Отправить заказ</button>
                            </form>
                 </div>
          </div>
     </div>
</div>

Обработчик отправки формы  send-zakazspec.php

<?php
$to  = "info@site.ru" ;

if ( isset( $_POST['phone'] ) ) {
  $name= substr( $_POST['name'], 0, 40 );
  $phone = substr( $_POST['phone'], 0, 15 );
  $model = substr( $_POST['model'], 0, 250);

  $body = "Имя: ".$name."\r\n";
  $body  .= "Телефон: ".$phone."\r\n";
  $body  .= "Спецтехника: ".$model."\r\n\r\n";

  send_mail($to, $body, $email);
}

// Вспомогательная функция для отправки почтового сообщения с вложением
function send_mail($to, $body, $email)
{
  $subject = 'Пользователь сайта SITE.RU заполнил форму Заказа спецтехники';
  $boundary = "--".md5(uniqid(time())); // генерируем разделитель
  $headers .= "From: ".$email."\r\n";   
  $headers .= "MIME-Version: 1.0\r\n";
  $headers .="Content-Type: multipart/mixed; boundary=\"".$boundary."\"\r\n";
  $multipart = "--".$boundary."\r\n";
  $multipart .= "Content-type: text/plain; charset=\"utf-8\"\r\n";
  $multipart .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";


  $body = $body."\r\n\r\n";
  $multipart .= $body;


  mail($to, $subject, $multipart, $headers);
}
?>