Как лучше всего выполнять небольшой клиентский JavaScript в Eleventy?

Проблема

Я хотел бы добавить немного клиентского JavaScript на свой сайт Eleventy. Кажется, я не могу получить доступ к document. с помощью Eleventy, что означает, что я не могу получить доступ к элементам и прослушивать их на предмет событий. Пример того, что не работает:

const formElement = document.querySelector("form")

Сообщение об ошибке, которое я получаю от Eleventy:

ReferenceError: document is not defined

Вопрос

Как мне работать с Eleventy, чтобы отслеживать изменения элементов документа и вносить изменения на страницу?

Пример:

formElement.addEventListener("change", function () {
    // Update nearby paragraph element based on form value
});

Мой реальный сценарий: я хотел бы, чтобы отображался элемент абзаца, который из form input type="radio" имеет значение checked.

Подходите так далеко

У меня есть файл в / data под названием fruit.json:

{
  "items": [
    {
      "name": "Apple"
    },
    {
      "name": "Banana"
    },
    {
      "name": "Strawberry"
    },
    {
      "name": "Mango"
    },
    {
      "name": "Peach"
    },
    {
      "name": "Watermelon"
    },
    {
      "name": "Blueberry"
    }
  ]
}

И файл HTML в / _includes / layouts на основе моего файла base.html:

{% extends "layouts/base.html" %}

{% block content %}

<form>
 {% for item in fruits.items %}
 {# Create a radio button for each, with the first one checked by default #}
 <input type="radio" name="screen" id="{{ item.name | slug }}" value="{{ item.name | slug }}" {% if loop.index === 1 %} checked {% endif %}>
    <label for="{{ item.name | slug }}">{{ item.name }}</label>
 {% endfor %}

{% set selectedFruit = helpers.getSelectedFruit() %}
<p>Currently selected item from above is: {{ selectedFruit }}</p>
</form>

{% endblock %}

Обратите внимание, что переменная с именем selectedFruit назначается вспомогательной функции:

{% set selectedScreen = helpers.getSelectedScreen() %}

Эта getSelectedScreen() функция выглядит так:

getSelectedScreen() {
    const formEl = document.querySelector("form")
    console.log(formEl)
}

Помимо того, что я не могу работать с .document, я чувствую, что этот подход, вероятно, «противоречит сути» Eleventy, генераторов статических сайтов в других отношениях:

  • Скрипт называется средним документом
  • Скрипт одноразовый и далек от контекста

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


person Danny    schedule 24.11.2020    source источник
comment
document, вероятно, недоступен из-за того, что содержимое сайта создается статически на стороне сервера. Что касается использования скриптов, есть довольно подробный раздел о файлах данных JavaScript.   -  person DigitalDrifter    schedule 24.11.2020


Ответы (1)


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

Ядро Eleventy, а также ваш .eleventy.js файл конфигурации написаны на JavaScript, который выполняется один раз на этапе сборки, то есть когда создается ваш статический сайт. Это происходит в среде NodeJS, а не в браузере, поэтому нет переменной document и DOM.

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

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

{% extends "layouts/base.html" %}

{% block content %}

<form id="fruits-form">
 {% for item in fruits.items %}
 {# Create a radio button for each, with the first one checked by default #}
 <input type="radio" name="screen" id="{{ item.name | slug }}" value="{{ item.name | slug }}" {% if loop.index === 1 %} checked {% endif %}>
    <label for="{{ item.name | slug }}">{{ item.name }}</label>
 {% endfor %}

  <p id="selected-fruits-output"></p>
</form>

{% endblock %}

Затем создайте файл JavaScript, который реагирует на события изменения в форме:

// fruit-form.js
const fruitForm = document.getElementById('fruits-form');
const formOutput = document.getElementById('selected-fruits-output');
fruitForm.addEventListener('change', e => {
    // update the formOutput with the list of selected fruits
});

Теперь вам нужно убедиться, что этот файл javascript скопирован в ваш выходной каталог, используя Сквозную копию файла:

eleventyConfig.addPassthroughCopy("path/to/fruit-form.js");

Наконец, не забудьте включить элемент скрипта в свой HTML-шаблон (убедитесь, что путь является абсолютным путем к выходным данным, как указано выше):

{# layouts/base.html #}
<script src="/path/to/fruit-form.js" defer></script>

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

person MoritzLost    schedule 24.11.2020
comment
Спасибо, @moritzlost. Это отличное описание как решения, так и того, как понять и подойти к времени сборки и клиентской стороне во время выполнения в JavaScript. Ситуация на стороне клиента во время выполнения блокирует доступ к _data / fruit.json в его текущей форме. Я бы хотел, чтобы эти данные также использовались в цикле Eleventy for, но это выходит за рамки моего вопроса, поэтому могу задать его в другом месте. - person Danny; 25.11.2020
comment
@Danny Вам просто нужно убедиться, что fruit.json также перемещен в выходной каталог, после чего вы можете динамически загружать его на стороне клиента с помощью JavaScript. Этот фрагмент должен нормально работать в вашем случае (измените имя файла в соответствии с его расположением в выходном каталоге). - person MoritzLost; 25.11.2020