Разработка шаблона для сайта на Squarespace 6

в 10:29, , рубрики: cms, web-разработка, Веб-разработка, метки:

Squarespace.com
SquareSpace — коммерческая CMS, объединяющая в себе удобный WYSIWYG-редактор, блог-платформу, функционал интернет-магазина и хостинг. Про все преимущества этого продукта надеюсь расскажет автор этого поста, как он и обещал — ну а я хотел бы поведать тот минимум, который поможет быстро сориентироваться при самостоятельной разработке сайта на Squarespace. Если вам интересно, как в кратчайшие сроки приступить к разработке — прошу под кат.

При создании сайта на SquareSpace можно воспользоваться более, чем дюжиной встроенных шаблонов. Все они очень хорошо проработаны, имеют мобильную версию. Благодаря настройкам и визуальному редактору (прямо на странице можно поменять и css-код) можно быстро добиться желаемого результата, и простые дизайны или несложные посадочные страницы получатся быстро, фактически просто клацая мышью. Но если вы любите все держать под контролем и понимать, что же творится «под капотом», да и дизайн сложный — то необходимо в настройках своего аккаунта (кроме случая, если вы сразу зарегистрировали платный) перейти на вкладку «Developer» и зарегистрироваться разработчиком.

Вот теперь-то можно приступать к разработке — ваш сайт теперь все время доступен, только по адресу расположена заглушка, предлагающая ввести капчу или логин/пароль для входа. Для простоты понимания при создании сайта можно воспользоваться «голым» шаблоном для разработчика, а можно пойти на маленькую хитрость — выбрать напоминающий будущий дизайн и функционал шаблон, а уж потом включить developer-режим сайта в настройках, что может существенно сократить время разработки (и подсмотреть как-что реализовано).

Включение developer-режима сайта

Включение developer-режима сайта

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

Основная информация после включения developer-режима

Основная информация после включения developer-режима

Итак, как видим, имеем два способа для подключения и отладки: Git и SFTP. Тут уж кому как удобнее (или как требует работодатель), но для несложных проектов лично мне проще пользоваться SFTP, хотя несомненный плюс Git — возможность по хорошо комментированным коммитам сориентироваться что же пошло не так и вернуться к более ранней версии. Так что просто настройте SFTP-соединение в вашей любимой IDE или редакторе и синхронизируйте локальную и удаленную версии (для пользователей IDEA и продуктов от JetBrainsвсе очень просто — Tools-> Deployment -> Type: SFTP -> Хост/Порт/Логин/Пароль -> выбрать корень сайта. Также не забудьте замаппить корень локального проекта на вкладке Mappings, если хотите включить Automatic Upload по Ctrl+S).

Все сайты, разрабатываемые на SquareSpace, по-умолчанию содержат: html5shiv.js, modernizr.js, и normalize.css, вещи нужные, заботится об этом нам не надо. Так же «из коробки» на стороне сервера работает Less-препроцессор — можно смело использовать синтаксис Less, можно создавать css-стили в разных файлах — пользователю сервер отдаст один объединенный css-файл. Тоже самое касается и Javascript — по-умолчанию используется фреймворк YUI3, но вы можете подключать свой любимый фреймворк или скрипты — на выходе они тоже будут минифицированы в один файл. Шаблонизатор по-умолчанию — Json-T.

Структура шаблона выглядит примерно так:

Структура шаблона

Структура шаблона

1.Главный конфигурационный файл template.conf

Я начну с корня проекта, так как именно здесь располагаются самые важные для отображения сайта настройки. Сердце шаблона — конфигурационный файл template.conf. В этом файле по желания задается имя шаблона и авторство, а также здесь указывается разметка (Layouts) вашего сайта, навигация (Navigation) по сайту и используемые стили для отображения. Выглядит это примерно так:

{
  "name" : "Something",
  "author" : "michael",

 "layouts" : {

        "Default" : {
            "name" : "Default",
            "regions" : [ "header", "content", "footer" ]
        },

        "Home" : {
            "name" : "Homepage",
            "regions" : [ "header", "contenthome", "events", "footer" ]
        },

        "SidebarLeft" : {
            "name" : "WithLeftSidebar",
            "regions" : [ "header", "contentwithsidebar", "footer" ]
        },

        "SidebarLeft2" : {
            "name" : "WithLeftSidebar2",
            "regions" : [ "header", "contentwithsidebar2", "footer" ]
        }
       
    },

  "navigations" : [ {
      "title" : "Main Navigation",
      "name" : "mainNav"
  }, {
      "title" : "Secondary Navigation",
      "name" : "secondaryNav"
  } ], 

  "stylesheets" : [ "base.less", "typography.less" ]

}

Содержимое template.conf

Как видим, уже на этом этапе желательно предполагать, как будут выглядеть страницы сайта, какой будет контент и так далее. Это позволит сразу выделить и задать нужные разметки и регионы (Regions) для страниц. В данном конкретном случае заданы такие layout'ы:

  • Default. В него будут подключены регионы «header», «content», «footer». Будет использоваться по-умолчанию при создании любых страниц
  • Home. Подключены «header», «contenthome», «events», «footer» регионы. Мне удобно было создать эту разметку отдельно для домашней страницы, так как она содержит отдельные блоки, которые больше нигде не повторяются и их дизайн сильно отличается от остальной части сайта
  • SidebarLeft Сюда включены регионы «header», «contentwithsidebar» и«footer»
  • SidebarLeft2По сути тоже, что и SidebarLeft, но содержимое собственно сайдбара сильно отличается — поэтому решено вынести это как отдельную разметку.

В дальнейшем, при создании страницы можно будет выбрать ту или иную разметку уже в визуальном режиме.

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

В последнем пункте файла template.conf вы указываете, какие стили из папки styles использовать. По умолчанию в этой папке уже лежит reset.css (он же normalise.css) и его указывать не нужно, он подключится и так.

2. Файлы регионов (Regions)

В простейшем случае, разметка может состоять из одного региона, назовем его site.region. Содержимое его было бы примерно следующим:

<!doctype html>
<html>

<head>
  {squarespace-headers}
</head>

<body class="{squarespace.page-classes}" id="{squarespace.page-id}">

  <header id="header">
    <h1><a href="/">{website.siteTitle}</a></h1>
    <squarespace:navigation navigationId="mainNav" template="navigation" />
  </header>

  <div id="canvas">

    <section id="page" role="main">
      {squarespace.main-content}
    </section>

    <aside id="sidebar">
      <squarespace:block-field id="sidebarBlocks" />
    </aside>

  </div>

</body>

</html>

Содержимое site.region

Не сильно вдаваясь в теги SquareSpace и синтаксис JSON-T и так становится ясно, что создается минимальная разметка страницы с шапкой и сайдбаром. Но я рекомендую функционально разграничивать отдельные области сайта — это делает проект более сопровождаемым, самому же легче потом вносить правки, не говоря о том, какое одолжение вы делаете человеку, которому придется работать с этим после/вместо вас.

Таким образом файл header.region мог бы выглядеть так:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
    <!--INCLUDE SQS SCRIPTS, META TAGS AND USER CONTENT FROM THE CODE INJECTION TAB-->
    {squarespace-headers}
</head>

<body id="{squarespace.page-id}" class="{squarespace.page-classes}">
    <!--HEADER-->
    <header class="navbar navbar-default navbar-static-top navbar-inverse">
        <div class="container fade-in">
             <squarespace:navigation navigationId="mainNav" template="nav" />
        </div>
    </header>

Файл header.region

Здесь конструкция {squarespace-headers} включает мета-теги для поисковиков и соцсетей, подключает squarespace-скрипты и позволяет дополнительно вставить код в визуальном режиме. Тегом <squarespace:navigation navigationId=«mainNav» template=«nav» /> подключается секция mainNav, используя как шаблон файл nav.block из каталога blocks.

Таким же образом создаются и остальные файлы *.region — создаете необходимую разметку и подключаете необходимый блок Squarespace. Этими блоками могут быть:

  • файлы *.block . Как и примере с header.region можно подключить, например, отдельную секцию навигации для подвала или сайдбара (указав соответственное имя)
  • теги Squarespace. Например, указав {squarespace.main-content} , подключим содержимое создаваемых вами страниц в визуальной части сайта.

    Среди самых полезны тегов я бы выделил такие:

    1. <squarespace:block-field id=«blockName» columns=«12» />. Создает пустой блок, указав количество columns=«12» мы растянем его на всю возможную ширину родительского элемента (да, у Squarespace 12-и колоночная grid-system как и у многих популярных фреймворков, я коснусь этого ниже). В этот пустой блок в визуальном режиме вы сможете позднее добавить любой из виджетов Squarespace или собственный html/js-код.
    2. <squarespace:query collection=«blog» limit=«3»></squarespace:query>. Сделает запрос к указанной коллекции (в данном случае blog), это может быть ваша галерея, блог, список товаров и тому подобное, и вернет указанное в limit количество объектов в JSON. Дальше их просто распарсить все тем же JSON-T и вывести на главной странице, в сайдбаре или где вздумается. В данное время кэширование запросов не организовано, рекомендуется не усердствовать с этим делом.

3. Блоки (Blocks)

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

navigation.block

<div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span>
            <span class="icon-bar"></span><span class="icon-bar"></span>
        </button>{.section website}<a class="navbar-brand" href="/">{.section logoImageUrl}<img src="{logoImageUrl}?format=original" alt="{siteTitle}"/>{.end}{.end}</a></div>
{.section items}
    <div class="collapse navbar-collapse navbar-ex1-collapse middle">
        <ul class="nav navbar-nav navbar-right">
            {.repeated section @}
            {.folder?}
            <li class="folder dropdown">
                <a class="{.if folderActive}folder-active{.end}" role="button" data-toggle="dropdown" href="#">{collection.navigationTitle}<span class="caret"></span></a>
                <ul class="dropdown-menu" role="menu">
                    {.repeated section items}
                    {.collection?}
                    <li class="{collection.typeLabel}-collection" role="presentation">
                        <a href="{collection.fullUrl}" class="{.section active}active{.end}" role="menuitem" tabindex="-1">{collection.navigationTitle}</a>
                    </li>
                    {.end}
                    {.section externalLink}
                    <li class="external-link">
                        <a href="{url}"{.section newWindow} target="_blank"{.end}>{title}</a>
                    </li>
                    {.end}
                    {.end}
                </ul>
            </li>
            {.or}
            {.collection?}
            <li class="{collection.typeLabel}-collection">
                <a href="{collection.fullUrl}" class="{.section active}folder-active{.end}">{collection.navigationTitle}</a>
            </li>
            {.end}
            {.section externalLink}
            <li class="external-link">
                <a href="{url}"{.section newWindow} target="_blank"{.end}>{title}</a>
            </li>
            {.end}
            {.end}
            {.end}
        </ul>
    </div>
{.end}

Файл navigation.block

Сразу оговорюсь, что здесь использовался Bootstrap 3, поэтому вы видите знакомые/незнакомые классы. Здесь основательно используется JSON-T-разметка, уточним же, что за что отвечает.

{.section website}<a class="navbar-brand" href="/">{.section logoImageUrl}<img src="{logoImageUrl}?format=original" alt="{siteTitle}"/>{.end}{.end}</a>

Этой конструкцией мы «открыли» глобальную область видимости нашего сайта, проверили задан ли логотип (загружается в визуальных настройках) и собственно отобразили его.

{.section items}
    <div class="collapse navbar-collapse navbar-ex1-collapse middle">
        <ul class="nav navbar-nav navbar-right">
            {.repeated section @}
            {.folder?}
            <li class="folder dropdown">
                <a class="{.if folderActive}folder-active{.end}" role="button" data-toggle="dropdown" href="#">{collection.navigationTitle}<span class="caret"></span></a>
                <ul class="dropdown-menu" role="menu">
                    {.repeated section items}
                    {.collection?}
                    <li class="{collection.typeLabel}-collection" role="presentation">
                        <a href="{collection.fullUrl}" class="{.section active}active{.end}" role="menuitem" tabindex="-1">{collection.navigationTitle}</a>
                    </li>
                    {.end}
                    {.section externalLink}
                    <li class="external-link">
                        <a href="{url}"{.section newWindow} target="_blank"{.end}>{title}</a>
                    </li>
                    {.end}
                    {.end}
                </ul>
            </li>

Здесь мы обращаемся конструкцией {.section items} ко всем нашим страницам на сайте, которые в визуальных настройках вы поместили в секцию навигации. Дальше включаем «цикл» — {.repeated section @} в котором будет опрашиваться каждый item (страница, папка со станицами, блог, коллекция, галерея, продукты или что-либо созданное вами дополнительно). Собственно, {.folder?} и {.collection?} и тестируют, чем есть текущий item.
Если это папка (folder), то создается ссылка на нее и запускается еще цикл {.repeated section items}, в котором происходит опрос теперь уже содержимого данной папки (а это могут быть либо просто страницы, либо коллекции, либо материалы с внешней ссылкой). Здесь следует заметить, что на данный момент поддерживается только один уровень вложенности, так что если у вас много уровней в меню, то нужно хитрить.
Удобный момент — можно протестировать активна ли данная папка или ссылка конструкциями {.if folderActive}folder-active{.end} и {.section active}active{.end} и присвоить им нужный класс (и описать нужными стилями). Очень удобно — не надо создавать свой велосипед со сравнением текущего адреса и адресов ссылок в навигации.

{.or}
            {.collection?}
            <li class="{collection.typeLabel}-collection">
                <a href="{collection.fullUrl}" class="{.section active}folder-active{.end}">{collection.navigationTitle}</a>
            </li>
            {.end}
            {.section externalLink}
            <li class="external-link">
                <a href="{url}"{.section newWindow} target="_blank"{.end}>{title}</a>
            </li>
            {.end}
            {.end}
            {.end}
        </ul>
    </div>
{.end}

Этот последний кусок кода делает то же самое, что мы провернули внутри цикла опроса папки — проверяет оставшиеся элементы, создает на них ссылки, задавая нужные классы и атрибуты (для внешних ссылок). Важно проследить, чтобы все секции были закрыты ({.end}), иначе не заработает правильно.

4. Коллекции (Collections)

Коллекции в Squarespace — это суть та же папка, только содержимое ее одного типа. Самый яркий пример — блог: все посты, по сути одинаковы, разное лишь их содержимое. В зависимости от шаблона, доступны разные коллекции, но у них всех одно устройство, разная лишь разметка внутри.
Итак, в идеале коллекция состоит из трёх файлов (рассматриваем блог):

  • blog.conf. Содержит основные настройки коллекции
  • blog.list. Отвечает за отображение списка всех элементов коллекции. В случае блога, это к примеру список кратких выдержек (excerpt) статей со ссылками на полную версию, пагинацией, если требуется.
  • blog.item. Отвечает за отображение конкретного элемента коллекции. В случае блога — полный текст статьи, ссылки на предыдущую/следующую статью, блок комментариев, соцкнопки и так далее.

Минимально для работы коллекции нужны файлы *.conf и *.item.

Дальше я приведу содержимое этих файлов без разъяснений, думаю если вы дочитались сюда, то уловили смысл построения шаблонов (комментарии и названия section говорят сами за себя).

blog.conf

{
  "title" : "Blog",
  "ordering" : "chronological",
  "addText" : "Add Post",
  "acceptTypes": ["text"],
  "promotedBlocks" : [ "" ]
  "icon": "blog"
}

Файл blog.conf

blog.item

{.repeated section items}

  <article class="{@|item-classes}" id="article-{id}" data-item-id="{id}">
    
    <div class="blog-wrapper">
  	  
      <div class="post{.passthrough?} link-list-item{.end}">
        
        <!--POST HEADER-->
  			<header>
          {.if title}<h2 class="entry-title" data-content-field="title">{.passthrough?}<a href="{sourceUrl}" target="_blank">{title}<span class="passthrough">→</span></a>{.or}<a href="{fullUrl}">{title}</a>{.end}</h2>{.end}
            </header>
          
        <!--POST BODY-->
        
        {.excerpt?}
        <div class="excerpt-content">{.main-image?}
            <div class="excerpt-thumb"><img {@|image-meta} /></div>{.end}
            {excerpt}<div class="clearfix"></div><a class="inline-read-more" href="{fullUrl}">Click to read more ...</a></div>
        <div class="clearfix"></div>
        {.or}
        {.section body}<div class="entry-content">{@}</div>{.end}
        {.end}

        <!--POST FOOTER-->
        <footer class="article-meta">
            <div class="article-author">
                {.section author}<span class="author"><a href="{collection.fullUrl}?author={id}" rel="author"><i class="icon-user"></i>{displayName}</a><span class="delimiter">|</span></span>{.end}
            </div>
           <div class="article-dateline">
                <i class="icon-calendar"></i><time class="published" datetime="{addedOn|date %F}">{addedOn|date %A, %B %d, %Y at %l:%M %p}</time><span class="delimiter">|</span>
           </div>
           <div class="shareLoveButtons">
               <a href="#">{@|social-button-inline}</a>
           </div>
           <div class="post-entry-injection">{postItemInjectCode}</div>
        </footer>
        
  	</div> <!-- / post --> 
  	
    </div><!-- / blog-wrapper -->
  </article>
<div class="clearfix"></div>
{.end}

<!--PAGINATION-->
{.section pagination}
<nav class="page-pagination">
  <div class="content-wrapper">
      {.if pagination.prevPage}<a href="{prevPageUrl}" id="prevLink"><i class="icon-chevron-left"></i> Previous |</a>
      {.or}<!-- <a href="{collection.fullUrl}"><i class="icon-list"></i>{collection.title}</a> -->{.end}
      <!--HOME PAGE-->
      <a href="{collection.fullUrl}"><i class="icon-list"></i> Main |</a>
      {.if pagination.nextPage}<a href="{nextPageUrl}" id="nextLink">Next <i class="icon-chevron-right"></i></a>
      {.or}<a href="{collection.fullUrl}"><i class="icon-list"></i>{collection.title}</a>{.end}
  </div>
</nav>
{.end}

Файл blog.list

blog.item

{.section pagination}
<nav class="blog-pagination">
    {.section prevItem}
    <a href="{fullUrl}"><i class="icon-chevron-left"></i> {title} |</a>
    {.or}
    <a class="disabled"></a>
    {.end}
    <!--HOME PAGE-->
        <a href="{collection.fullUrl}"><i class="icon-list"></i> Main |</a>
    <!--OLDER PAGE-->
        {.section nextItem}
         <a href="{fullUrl}">{title} <i class="icon-chevron-right"></i></a>
        {.or}
         <a class="disabled">End off posts</a>
        {.end}
</nav>
{.end}
{.section item}
<article class="{@|item-classes}" id="article-{id}" data-item-id="{id}">
    
    <div class="blog-wrapper item">

        <div class="post{.passthrough?} link-list-item{.end}">
        <!--POST HEADER-->
            <header>
                {.if title}<h2 class="entry-title" data-content-field="title">{.passthrough?}<a href="{sourceUrl}" target="_blank">{title}<span class="passthrough">→</span></a>{.or}<a href="{fullUrl}">{title}</a>{.end}</h2>{.end}
            </header>

            <!--POST BODY-->
    <!--POST BODY-->
          
      {.section body}<div class="entry-content">{@}</div>{.end}

    <!--POST FOOTER-->
      <footer class="article-meta">
          <div class="article-author">
              {.section author}<span class="author"><a href="{collection.fullUrl}?author={id}" rel="author"><i class="icon-user"></i>{displayName}</a><span class="delimiter">|</span></span>{.end}
          </div>
          <div class="article-dateline">
              <i class="icon-calendar"></i><time class="published" datetime="{addedOn|date %F}">{addedOn|date %A, %B %d, %Y at %l:%M %p}</time><span class="delimiter">|</span>
          </div>
          <div class="shareLoveButtons">
              <a href="#">{@|social-button-inline}</a>
          </div>
          <div class="clearfix"></div>
          <div class="post-entry-injection">{postItemInjectCode}</div>
         </footer>
    </div><!-- /post -->
  </div><!-- /content-wrapper -->
  </article>

    <div class="clearfix"></div>

{.end}
<!--PAGINATION-->

{.section pagination}
<nav class="blog-pagination">
    {.section prevItem}
    <a href="{fullUrl}"><i class="icon-chevron-left"></i> {title} |</a>
    {.or}
    <a class="disabled"></a>
    {.end}
    <!--HOME PAGE-->
    <a href="{collection.fullUrl}"><i class="icon-list"></i> Main |</a>
    <!--OLDER PAGE-->
    {.section nextItem}
    <a href="{fullUrl}">{title} <i class="icon-chevron-right"></i></a>
    {.or}
    <a class="disabled">End off posts</a>
    {.end}
</nav>
{.end}
{.section item}
<!--COMMENTS-->
{.comments?}
<div class="comments">{@|comments}</div>
{.end}
{.end}

Файл blog.item

5. Стили (каталог Styles)

В первую очередь отмечу, что Squarespace имеет свою сетку из 12-ти колонок. Разметка подобна Bootstrap, так row = sqs-row, col-md-4 = sqs-col-4. Так что вполне можно пользоваться в разметке этим фактом, но я не встречал в документации описания этих встроенных стилей, поэтому нужно «доходить» самому. И даже если вы верстаете свои стили или используете какой-то фреймворк — Squarespace «любит» оборачивать ваши разметку своими блоками — в целом это совсем не мешает, но если что-то идет не так, как задумано, просто посмотрите в инспекторе — а не добавилось ли что-то в разметке. В остальном — все обычно, используете валидный LESS или CSS-синтаксис.

Фишкой является то, что в стилях можно использовать твики (tweaks), которые дают возможность изменять указанные вами параметры в заданных пределах в визуальном редакторе стилей, встроенном в Squarespace. Очень удобно — клиент сам потом может что-то подкрутить, настроить, да и самому часто проще перенастроить что-то именно так, а не цепляться по SFTP.
В основе твиков — LESS-переменные и JSON-объекты, в которых и заданы нужные параметры и границы. Смотрим:

// tweak: { "type" : "value", "title" : "Logo Size", "min" : 50, "max" : 150 }
@logoHeight: 75px;

.logo {
  max-height: @logoHeight;
}

Твик для изменения размеров логотипа

Или разрешим менять цвет (будет всплывать удобный Color picker):

// tweak: { "type" : "color", "title" : "Text Color" }
@textColor: #444444;

body {
  color: @textColor;
}

Твик для изменения цвета текста

Твики можно группировать по свойствам и категориям — в результате в визуальном редакторе получите нечто подобное:

Style Editor

Style Editor

6. Ассеты (Assets)

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

7. Скрипты (Scripts)

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

<squarespace:script src="plugin.js" combo="true" />
<squarespace:script src="site.js" combo="true" />

Загрузка скриптов через загрузчик

8.Страницы (Pages)

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

Заключение

Squrespace очень удобная платформа, вполне может рассматриваться как вариант, если Ваш клиент готов платить. При этом Вы получаете удобный инструмент, который обеспечивает уменьшение сроков разработки и удобный сервис.

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

Автор: michaelmashay

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js