Определение шаблона HTML для добавления с помощью JQuery

125

У меня есть массив, который я просматриваю. Каждый раз, когда условие выполняется, я хочу добавить копию приведенного HTMLниже кода в элемент контейнера с некоторыми значениями.

Где я могу разумно использовать этот HTML-код повторно?

<a href="#" class="list-group-item">
    <div class="image">
         <img src="" />
    </div>
    <p class="list-group-item-text"></p>
</a>

JQuery

$('.search').keyup(function() {
    $('.list-items').html(null);

    $.each(items, function(index) {
        // APPENDING CODE HERE
    });
});
Патрик Рек
источник
2
Если вы ищете умный способ, поместите всю свою информацию в один DIV и стилизуйте его. Не создавайте множество таблиц, состоящих только из двух ячеек, не оборачивайте их в якоря.
Сергей Снегирев
3
OP, очевидно, заинтересован в правильной практике кодирования (спасибо!), Поэтому я хотел помочь. Если бы я хотел внести свой вклад в реальную проблему, я бы отправил ответ. Я уверен, что кто-то согласится с тем, что использование полноценной таблицы из двух ячеек для размещения изображения и некоторого текста едва ли оправдано, за исключением некоторых действительно экзотических требований (IE4?)
Сергей Снегирев
1
@BrianG. Однако публикация комментария на самом деле не означает, что вы отклоняетесь от темы. Хотя я согласен с тем, что комментарии являются подходящим местом для них (если они все еще актуальны), они также подходят для подсказок от людей, у которых еще нет времени, чтобы развернуть их в ответ. OP полезно прояснить, что вы делаете.
millimoose
Никто не ответил на твой вопрос, Патрик?
Себастьян Нейра

Ответы:

164

Вы можете решить использовать в своем проекте механизм шаблонов, например:

Если вы не хотите включать другую библиотеку, John Resig предлагает решение jQuery , подобное приведенному ниже.


Браузеры и программы чтения с экрана игнорируют нераспознанные типы скриптов:

<script id="hidden-template" type="text/x-custom-template">
    <tr>
        <td>Foo</td>
        <td>Bar</td>
    <tr>
</script>

При использовании jQuery добавление строк на основе шаблона будет напоминать:

var template = $('#hidden-template').html();

$('button.addRow').click(function() {
    $('#targetTable').append(template);
});
Матис Флитстра
источник
@MaksimLuzik прав. Усы (обратите внимание на написание товарного знака) легче и подключаются к коду OP, но Handlebars имеет больше функций для обработки массивов и в конечном итоге может предоставить более подходящее решение. Вот хорошая сравнительная статья: blog.cubettech.com/…
Майкл Шепер,
13
Пример того, как редактировать шаблон здесь: jsfiddle.net/meehanman/7vw8bc84
Дин Михан
176

Старый вопрос, но поскольку вопрос спрашивает «с использованием jQuery», я подумал, что предоставлю вариант, который позволит вам сделать это, не вводя зависимости от поставщика.

Хотя существует множество движков шаблонов, многие из их функций в последнее время перестали пользоваться популярностью, при этом iteration ( <% for), conditionals ( <% if) и transforms ( <%= myString | uppercase %>) рассматриваются в лучшем случае как микроязык, а в худшем - как анти-шаблоны. Современные методы создания шаблонов поощряют простое отображение объекта в его представление DOM (или другое), например, то, что мы видим со свойствами, сопоставленными с компонентами в ReactJS (особенно с компонентами без состояния).

Шаблоны внутри HTML

Одно свойство, на которое вы можете положиться для сохранения HTML-кода вашего шаблона рядом с остальной частью вашего HTML, - это использование неисполняемого <script> type, например <script type="text/template">. Для вашего случая:

<script type="text/template" data-template="listitem">
    <a href="${url}" class="list-group-item">
        <table>
            <tr>
                <td><img src="${img}"></td>
                <td><p class="list-group-item-text">${title}</p></td>
            </tr>
        </table>
    </a>
</script>

При загрузке документа прочтите свой шаблон и токенизируйте его, используя простой String#split

var itemTpl = $('script[data-template="listitem"]').text().split(/\$\{(.+?)\}/g);

Обратите внимание, что с нашим токеном вы получаете его в альтернативном [text, property, text, property]формате. Это позволяет нам красиво отображать его Array#mapс помощью функции сопоставления:

function render(props) {
  return function(tok, i) { return (i % 2) ? props[tok] : tok; };
}

Куда propsмогло быть похоже { url: 'http://foo.com', img: '/images/bar.png', title: 'Lorem Ipsum' }.

Собираем все вместе, предполагая, что вы проанализировали и загрузили свой, itemTplкак указано выше, и у вас есть itemsмассив в области видимости:

$('.search').keyup(function () {
  $('.list-items').append(items.map(function (item) {
    return itemTpl.map(render(item)).join('');
  }));
});

Этот подход также едва ли соответствует jQuery - вы должны иметь возможность использовать тот же подход, используя ванильный javascript с document.querySelectorи .innerHTML.

jsfiddle

Шаблоны внутри JS

Задайте себе вопрос: действительно ли вам нужно / нужно определять шаблоны как файлы HTML? Вы всегда можете разбить на компоненты + повторно использовать шаблон так же, как вы бы повторно использовали большинство вещей, которые хотите повторить: с функцией.

В es7-land, используя деструктуризацию, строки шаблонов и стрелочные функции, вы можете написать совершенно красивые функции компонентов, которые можно легко загрузить с помощью $.fn.htmlописанного выше метода.

const Item = ({ url, img, title }) => `
  <a href="${url}" class="list-group-item">
    <div class="image">
      <img src="${img}" />
    </div>
    <p class="list-group-item-text">${title}</p>
  </a>
`;

Тогда вы могли бы легко отобразить его, даже если он был отображен из массива, например:

$('.list-items').html([
  { url: '/foo', img: 'foo.png', title: 'Foo item' },
  { url: '/bar', img: 'bar.png', title: 'Bar item' },
].map(Item).join(''));

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

Джош из Карибу
источник
1
Хороший шаблонизатор.
Артур Кастро,
41
Ваши шаблоны в разделе JS - это бомба
BigRon
2
Не могли бы вы добавить демонстрацию / скрипт для подхода "Шаблоны в HTML"?
LCJ
1
Вот Это Да! Шаблон внутри JS ... Никогда не думал об этом! Удивительный!
Роман Лопес
2
Вау, это действительно очень мило. Я добавляю атрибут data-url к каждому шаблону. И получение данных ajax для каждого из них. Это так мило и сэкономило мне столько времени! Спасибо.
Дэйви
42

Вместо этого используйте HTML-шаблон!

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

Я сделал демонстрацию, чтобы показать вам, как использовать его в действии и как вставлять один шаблон в другой, редактировать и затем добавлять в DOM документа.

пример html

<template id="mytemplate">
  <style>
     .image{
        width: 100%;
        height: auto;
     }
  </style>
  <a href="#" class="list-group-item">
    <div class="image">
      <img src="" />
    </div>
    <p class="list-group-item-text"></p>
  </a>
</template>

пример js

// select
var t = document.querySelector('#mytemplate');

// set
t.content.querySelector('img').src = 'demo.png';
t.content.querySelector('p').textContent= 'demo text';

// add to document DOM
var clone = document.importNode(t.content, true); // where true means deep copy
document.body.appendChild(clone);

HTML <шаблон>

  • + Его содержимое эффективно инертно до активации. По сути, ваша разметка скрыта DOM и не отображается.

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

  • + Контент считается отсутствующим в документе. Использование document.getElementById()или querySelector()на главной странице не вернет дочерние узлы шаблона.

  • + Шаблоны могут быть размещены в любом месте внутри <head>, <body>или <frameset>и может содержать любой тип контента , который разрешен в этих элементах. Обратите внимание, что "где угодно" означает, что его <template>можно безопасно использовать в местах, запрещенных анализатором HTML.

Отступать

Поддержка браузера не должна быть проблемой, но если вы хотите охватить все возможности, вы можете легко проверить:

Для обнаружения функции <template>создайте элемент DOM и убедитесь, что свойство .content существует:

function supportsTemplate() {
  return 'content' in document.createElement('template');
}

if (supportsTemplate()) {
  // Good to go!
} else {
  // Use old templating techniques or libraries.
}

Некоторые сведения о методе сценария перегрузки

  • + Ничего не отображается - браузер не отображает этот блок, потому что <script>тег имеет display:noneпо умолчанию.
  • + Inert - браузер не анализирует содержимое скрипта как JS, потому что его тип установлен не на "text/javascript".
  • -Вопросы безопасности - поощряет использование .innerHTML. Анализ строки во время выполнения данных, предоставленных пользователем, может легко привести к уязвимости XSS.

Полная статья: https://www.html5rocks.com/en/tutorials/webcomponents/template/#toc-old

Полезная ссылка: https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode http://caniuse.com/#feat=queryselector

СОЗДАНИЕ ВЕБ-КОМПОНЕНТОВ Руководство по созданию пользовательских веб-компонентов с использованием HTML-шаблонов от Trawersy Media: https://youtu.be/PCWaFLy3VUo

DevWL
источник
4
templateЭлемент не поддерживается Internet Explorer (по состоянию на 2018 год, IE11). Протестировано с использованием примера 580 из w3c.github.io/html/single-page.html .
Роланд
Мне нравится <template>, хотя я бы рекомендовал свободно использовать классы, чтобы было ясно, какие реквизиты для чего устанавливаются. Для правильного отображения ваших данных в шаблоне по-прежнему требуются некоторые навыки js, особенно с использованием подхода в стиле компонентов для сопоставления больших наборов данных. Лично мне по-прежнему нравится просто создавать HTML в функциях или, в идеале, строить DOM непосредственно с помощью самого JS и вообще отказываться от HTML (например, как это делает React).
Джош из Карибу,
Мне понравился шаблонный подход. Одна из моих рекомендаций - сначала клонировать шаблон (а не в конце) и работать с клонированным объектом. В противном случае вносимые вами изменения коснутся самого шаблона. Если бы у вас была условная логика, в которой вы устанавливали некоторые свойства / контент только в некоторых случаях, эти свойства присутствовали бы при следующем использовании шаблона. Клонируя перед каждым использованием, вы гарантируете, что у вас всегда будет стабильная база для работы.
jt.
13

Добавить где-нибудь в теле

<div class="hide">
<a href="#" class="list-group-item">
    <table>
        <tr>
            <td><img src=""></td>
            <td><p class="list-group-item-text"></p></td>
        </tr>
    </table>
</a>
</div>

затем создайте css

.hide { display: none; }

и добавьте в свой js

$('#output').append( $('.hide').html() );
Матеуш Новак
источник
1
OP имеет JSON и хочет отобразить некоторый html, используя этот массив в качестве источника данных. Как этот ответ решает проблему? Ваш ответ берет html-узел и клонирует / дублирует его, и ничего, связанного с привязкой данных.
Адриан Ифтоде,
Так что я голосую против.
Адриан Ифтоде,
Я действительно думаю, что вы не помогаете получить лучший ответ, поскольку вы могли бы начать с напоминания нам о том, что вопрос был шире, чем то, что было рассмотрено в наших ответах.
Себастьян Нейра,
1
Это действительно работает, но это более эффективно .children().clone(), чем .html(). Скорость около 3: 1 для случайного, которое <tr/>я имел на странице, использующей этот код i=10000; time=performance.now(); while (--i) {$a.clone().append(0 ? $b.html() : $b.children().clone())} performance.now()-time. Это соотношение на самом деле немного более преувеличено, потому что я использую $ a.clone (), но попытка очистить его на каждой итерации больше снижает производительность, чем клонирование, поэтому я не уверен, как сделать его более точным, потому что Функции синхронизации имеют свою стоимость.
Chinoto Vokro
1
Это плохой подход. в основном потому, что содержимое шаблона загружается и анализируется, даже если вы его не использовали. Это слишком большая проблема, чтобы считать ее правильным ответом. Также, как и кто-то другой выше, если вам нужно хотя бы использовать clone вместо .html ()
DevWL
3

Другая альтернатива: Pure

Я использую его, и он мне очень помог. Пример, показанный на их сайте:

HTML

<div class="who">
</div>

JSON

{
  "who": "Hello Wrrrld"
}

результат

<div class="who">
  Hello Wrrrld
</div>
alditis
источник
2

Для решения этой проблемы я признаю два решения:

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

    $.get('url/to/template', function(data) {
        temp = data
        $('.search').keyup(function() {
            $('.list-items').html(null);
    
            $.each(items, function(index) {
                 $(this).append(temp.clone())
            });
    
        });
    });

    Учтите, что событие должно быть добавлено после завершения ajax, чтобы быть уверенным, что данные доступны!

  • Второй - напрямую добавить его в любом месте исходного html, выбрать его и скрыть в jQuery:

    temp = $('.list_group_item').hide()

    После добавления нового экземпляра шаблона вы можете

    $('.search').keyup(function() {
        $('.list-items').html(null);
    
        $.each(items, function(index) {
            $(this).append(temp.clone())
        });
    });
  • То же, что и предыдущий, но если вы не хотите, чтобы шаблон оставался там, а только в javascript, я думаю, вы можете использовать (не тестировали!) .detach()Вместо hide.

    temp = $('.list_group_item').detach()

    .detach() удаляет элементы из DOM, сохраняя при этом данные и события (.remove () нет!).

Себастьян Нейра
источник
-- Без проблем! -
iConnor
Операция имеет JSON и хочет отобразить некоторый html, используя этот массив в качестве источника данных. Как этот ответ решает проблему? Ваш ответ берет html-узел и клонирует / дублирует его, и ничего, связанного с привязкой данных.
Адриан Ифтоде,
1
Цитирую из op: я хочу добавить копию HTML-кода ниже к элементу контейнера с некоторыми значениями. Я считаю, что это решает его проблему.
Себастьян Нейра,
А как трактуется часть "некоторые ценности"?
Адриан Ифтоде,
Что ж, это не значит, что это не решает проблему. Я ценю, что вы сказали мне, что я упустил один из пунктов :) В любом случае, как мне узнать, как вставить эти значения, если я даже не знаю, о каких значениях мы говорим? Просто взгляните на эту часть. Где я могу разумно использовать этот HTML-код повторно? , Что на самом деле спрашивают? О JSON или включении HTML?
Себастьян Нейра,
1

Очень хороший ответ от DevWL об использовании собственного элемента шаблона HTML5 . Чтобы внести свой вклад в этот хороший вопрос из OP, я хотел бы добавить, как использовать этот templateэлемент с помощью jQuery, например:

$($('template').html()).insertAfter( $('#somewhere_else') );

Содержимое templateне html, а просто обрабатывается как данные, поэтому вам нужно обернуть содержимое в объект jQuery, чтобы затем получить доступ к методам jQuery.

Жак
источник