Можно ли автоматически объявлять тысячи переменных в JavaScript?

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

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

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

var sent1 = document.getElementById('s1');
var sent1info = document.getElementById('s1info');
var sent2 = document.getElementById('s2');
var sent2info = document.getElementById('s2info');
var sent3 = document.getElementById('s3');
var sent3info = document.getElementById('s3info');
...

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

Для контекста мое намерение с каждой переменной состоит в том, чтобы передать ее в эту функцию:

sent1.onclick = function(){
    if (sent1info.className == 'open'){
        sent1info.className = 'close';
    } else{
        sent1info.className = 'close';
    }
};

Отсюда CSS уменьшит информационное поле до высоты 0, когда имя класса «закрыто», и расширит его, когда имя класса «открыто». Но, опять же, это потребует от меня написания этой функции тысячи раз.

Есть ли способ сделать это автоматически? Или я все неправильно делаю?

Изменить, чтобы показать HTML:

<!DOCTYPE html>
<html>
<head>...</head>
<body>
    <div id = "everything">
        <header id = "theheader" class = "clearfix">...</header>
        <div id = "thebody" class = "box clearfix">
            <aside id = "page" class = "side">...</aside>
            <div class = "items">
                <article id = "content" class = "article">
                    <img id = "sentpic" src = "sentpic.jpg">
                    <h1>Sentences</h1>
                    <div id = "sentences">
                        *** This is where the JS inserts sentences and information ***
                        <ul id='sent1' class='sentcontent'><li class='number'>1.</li><li class='thesent'>...</li></ul>
                        <div id='sent1info' class='infobox'>
                            <ul class='sentinfo'><li class='information'>Info:</li><li class='infotext'><em>...</em></li></ul>
                            <ul class='sentinfo'><li class='information'>Line:</li><li class='line'>...</li></ul>
                        </div>
                        <ul id='sent2' class='sentcontent'><li class='number'>2.</li><li class='thesent'>...</li></ul>"
                        <div id='sent2info' class='infobox'>
                            <ul class='sentinfo'><li class='information'>Info:</li><li class='infotext'><em>...</em></li></ul>
                            <ul class='sentinfo'><li class='information'>Line:</li><li class='line'>...</li></ul>
                        </div>
                        *** it goes on like this for each sent inserted ***
                    </div>
                </article>
            </div>
        </div>
        <div class = "associates clearfix">...</div>
        <footer class = "foot">...</footer>
    </div>
    <script type = "text/javascript" src = "index.js"></script>
</body>
</html>

почему бы не использовать цикл forEach или for?

Josh Adams 10.02.2019 19:58

Просмотрите массивы.. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

Keith 10.02.2019 19:59

Просто индексируйте элементы, имеющие общий класс, по индексу в массиве предложений. Покажите, как вы генерируете html из данных

charlietfl 10.02.2019 20:00

Было бы полезно увидеть созданную структуру HTML. Например: являются ли предложение и деталь одним и тем же родителем? Они где-то еще на странице? Кроме того, какова функция, которая генерирует это? Если вы отредактируете свой вопрос, включив в него эти вещи, вы, вероятно, получите лучший ответ.

Bryce Howitson 10.02.2019 20:11

Не могли бы вы показать фрагмент HTML, который вы используете в настоящее время?

Roko C. Buljan 10.02.2019 20:12

Кроме того, вы создаете HTML динамически (используя JS)?

Roko C. Buljan 10.02.2019 20:13
onClick занимает 3-4 строки, чтобы установить className в closed во всех случаях if/else. Просто установите closed.
radarbob 10.02.2019 20:48

ОК, спасибо за предоставление контекста HTML-обертки. Теперь понятно, что вы вставляете элементы «предложения» динамически. Не могли бы вы теперь показать HTML для раскрывающихся списков предложения? Так что просто укажите один из "Здесь JS вставляет предложения."

Roko C. Buljan 10.02.2019 20:52

@AdeDoyle Проверьте ответ ниже, который показывает альтернативный подход к добавлению/удалению имен классов с динамически добавляемыми элементами.

AndrewL64 10.02.2019 21:04

@RokoC.Buljan RokoC.Buljan Я добавил это сейчас. Я должен был сделать это в первую очередь, но мне пришлось копаться в JS, чтобы найти.

AdeDoyle 10.02.2019 21:28

@AdeDoyle, да, хорошо! Спасибо - добавил ответ в надежде :)

Roko C. Buljan 10.02.2019 21:31
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
11
151
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Используя HTML-элемент <details>:

const json = [
  {thesent:"Lol", info:"This is some info 1", line:"Whatever 1..."},
  {thesent:"Lorem", info:"Some info 2", line:"Something here 2..."},
];

const template_sentence = (ob, i) => `
<details class = "sentence">
  <summary>${i+1} ${ob.thesent}</summary>
  <h3>${ob.info}</h3>
  <div>${ob.line}</div>
</details>`;

document.querySelector("#sentences").innerHTML = json.map(template_sentence).join('');
<div id = "sentences"></div>

В противном случае, используя вашу текущую несемантическую разметку:

Таргетинг по ID (в вашем конкретном случае) не нужен. Существуют и другие методы, такие как + Next Adjacent селектор брата и сестры в CSS.

А вот пример JS — он не требует пояснений, но не стесняйтесь спрашивать.

  • Используйте JS, чтобы переключить класс (.active в этом примере) на интерактивный элемент UL.
  • Используйте CSS и селектор следующего соседнего брата +, чтобы сделать DIV Информацияdisplay: block

/* Just a sample... you'll know how to modify this with the right properties I hope */
const json = [
  {thesent:"Lol", info:"This is some info 1", line:"Whatever 1..."},
  {thesent:"Lorem", info:"Some info 2", line:"Something here 2..."},
];

// The toggle function:
const toggleInfobox = ev => ev.currentTarget.classList.toggle("active");

// A single sentcontent template
const template_sentence = (ob, i) =>
`<ul class='sentcontent'>
    <li class='number'>${i+1}</li>
    <li class='thesent'>${ob.thesent}</li>
  </ul>
  <div class='infobox'>
    <ul class='sentinfo'>
      <li class='information'>Info:</li>
      <li class='infotext'><em>${ob.info}</em></li>
    </ul>
    <ul class='sentinfo'>
      <li class='information'>Line:</li>
      <li class='line'>${ob.line}</li>
    </ul>
</div>`;

// Get target element
const el_sentences = document.querySelector("#sentences");

// Loop JSON data and create HTML
el_sentences.innerHTML = json.map(template_sentence).join('');

// Assign listeners
const el_sentcontent = el_sentences.querySelectorAll(".sentcontent");
el_sentcontent.forEach(el => el.addEventListener('click', toggleInfobox));
/* BTW, why do you use <ul> ? That's not a semantic list! */
.sentcontent { padding: 0; cursor: pointer;}
.sentcontent li { display: inline-block; }

/* Arrows are cool, right? */
.sentcontent:before        { content: "\25BC"; }
.sentcontent.active:before { content: "\25B2"; }

/* Hide adjacent .infobox initially, 
/* and show adjacent .infobox on JS click */
.sentcontent        + .infobox { display: none; }
.sentcontent.active + .infobox { display: block; }
<div id = "sentences"></div>

В этом Ответ на переполнение стека вы можете узнать больше о переключении элемента при нажатии какой-либо кнопки.

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

Andy Hoffman 10.02.2019 22:19

@AndyHoffman Я согласен с тем, что идентификаторы полезны, когда нужно запросить параметризацию якорей, чтобы разрешить прокрутку, как в: //example.com#sent1 или //example.com#sent1content, но ни ответы, ни вопрос не предполагают такого поведения - не говоря уже о describedby aria и т. д. - Это еще одно расширение этого ответа - возможно, если ОП интересуется этой темой...

Roko C. Buljan 10.02.2019 22:24

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

Andy Hoffman 10.02.2019 22:25

Поскольку браузеры сопоставляют селекторы CSS справа налево, эта строка: .sentcontent + .infobox { display: none; } может быть оптимизирована до .infobox { display: none; }.

Andy Hoffman 10.02.2019 22:34

@AndyHoffman да ... в качестве общего примечания ... но важно инкапсулировать поведение. .infobox может появиться в другом месте DOM. Мы не хотим display: none; для этих элементов, только те, которые в какой-то момент обрабатываются точным воссоединением при выборе родственного элемента В:.sentcontent + .infobox.sentcontent.active + .infobox — Разделение задач и т. д. Большая тема. Так что нет. Предложенный вариант абсолютно предпочтителен, на мой взгляд.

Roko C. Buljan 10.02.2019 22:39
Ответ принят как подходящий

Этот вопрос больше относится к архитектуре, чем к необходимости создания динамических переменных. Рассмотрим этот пример:

  • ids удалены (используются существующие имена классов)
  • Этот шаблон масштабируется для экземпляров предложения n
  • В handleClick мы переключаем класс open на элемент, по которому щелкнули, что позволяет нам использовать селектор соседнего брата через CSS.
  • Нет необходимости в классе close, так как отсутствие класса open означает закрытое состояние.

let outerUL = document.querySelectorAll('.sentcontent')

function handleClick() {
  this.classList.toggle('open');
}

outerUL.forEach(ul => {
  ul.addEventListener('click', handleClick);
})
.sentcontent {
  cursor: pointer;
}

.sentcontent.open + .infobox {
   display: block;
}

.infobox {
  background-color: #eee;
  display: none;
  padding: .25em .5em;
}
<ul class='sentcontent'>
  <li class='number'>1.</li>
  <li class='thesent'>Sent</li>
</ul>
<div class='infobox'>
  <ul class='sentinfo'>
    <li class='information'>Info</li>
    <li class='infotext'><em>Info text</em></li>
  </ul>
  <ul class='sentinfo'>
    <li class='information'>Line info</li>
    <li class='line'>Line</li>
  </ul>
</div>

<ul class='sentcontent'>
  <li class='number'>2.</li>
  <li class='thesent'>Sent</li>
</ul>
<div class='infobox'>
  <ul class='sentinfo'>
    <li class='information'>Info</li>
    <li class='infotext'><em>Info text</em></li>
  </ul>
  <ul class='sentinfo'>
    <li class='information'>Line info</li>
    <li class='line'>Line</li>
  </ul>
</div>

https://jsfiddle.net/d91va7tq/2/

Энди, теперь это выглядит круто! (PS: было бы неплохо, вместо переключения класса на элемент DIV, переключать класс на элемент target/trigger! Таким образом, мы можем не только управлять DIV display с помощью оператора +, но и изменять стили для триггера !) Молодец

Roko C. Buljan 10.02.2019 22:06

@RokoC.Buljan Хороший совет. Сделанный.

Andy Hoffman 10.02.2019 22:12

@AndyHoffman Мне трудно заставить это работать. Нужна ли мне загрузка где-то там? Когда я тестирую, все загружается, но щелчок на самом деле ничего не делает.

AdeDoyle 10.02.2019 23:00

@AdeDoyle Поместите JavaScript непосредственно перед </body> внизу разметки: <script>js code here</script>.

Andy Hoffman 10.02.2019 23:02

@AdeDoyle Если это поможет, вот полная демонстрация (через Plunkr) со всем вашим исходным кодом (плюс мой).

Andy Hoffman 10.02.2019 23:08

@AndyHoffman У меня уже есть <script type = "text/javascript" src = "index.js"></script> прямо перед </body>, указывающий на файл, в котором находится JS. Пробовал ставить напрямую, просто для уверенности, но безрезультатно.

AdeDoyle 10.02.2019 23:12

@AdeDoyle Я обновил плункр демо, чтобы использовать внешний файл script, index.js. Как видите, я пропустил type = "text/javascript", потому что он больше не нужен.

Andy Hoffman 10.02.2019 23:18

@AdeDoyle Возможно, вы добавляли теги <script></script> во внешний файл, что могло привести к его поломке.

Andy Hoffman 10.02.2019 23:23

@AndyHoffman Я думаю, это как-то связано с тем, что внешний файл также используется для динамической вставки всех предложений из документа .json. Если бы это было полезно, я мог бы создать ветку на плункере и загрузить полный html и, например, данные json?

AdeDoyle 10.02.2019 23:29

@AdeDoyle Это имеет смысл.

Andy Hoffman 10.02.2019 23:30

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

AdeDoyle 11.02.2019 00:52

@AdeDoyle Думаю, мы готовы! Посмотрите здесь.

Andy Hoffman 11.02.2019 00:58

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

AdeDoyle 11.02.2019 01:18

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

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

const json = [
  {thesent:"Lol", info:"This is some info 1", line:"Whatever 1..."},
  {thesent:"Lorem", info:"Some info 2", line:"Something here 2..."},
];

const container = document.querySelector(".container");
json.forEach((item)=> {
let x= item;
let el = document.createElement("li");
el.innerHTML = x.thesent;
container.appendChild(el);
el.addEventListener("click",()=> {
var infoContainer= el.querySelector(".info");
// dont create all html element at once, instead create them 
//when the user click on it. this is better when you have a very large data.
if (!infoContainer){ // not created, then create  
    infoContainer = document.createElement("div");
    infoContainer.className = "info";
    var info = document.createElement("div");
    var line = document.createElement("div");
    info.innerHTML = x.info;
    line.innerHTML = x.line;
    infoContainer.appendChild(info);
    infoContainer.appendChild(line);
    el.appendChild(infoContainer);
} else if (infoContainer.style.display == "none") // created and hidden, then display it 
           infoContainer.style.display = "block";
  else infoContainer.style.display= "none"; // already displayed then hide it 
});
})
.container li >div.info >div:first-child{
font-size: 12px;

}

.container li >div.info >div:last-child{
font-size: 10px;

}
<ul class = "container">

</ul>

Не совсем та разметка, которую использует OP. Он использует UL (странным образом, но да... :) - Хорошая идея насчет рендерить когда надо!

Roko C. Buljan 10.02.2019 22:01

Я скопировал способ, которым сделал навигационное меню в шапке. Теперь я понимаю, что должен был использовать табличный формат (да?), но я не очень хорошо разбираюсь в JS, и технически это работает. Я мог бы реализовать это как часть исправления.

AdeDoyle 10.02.2019 22:27

Спасибо @Roko C. Buljan, у меня был некоторый опыт в таких вещах. а насчет markup ему все равно надо его переписать. Поэтому я хотел, чтобы он понял, как правильно или хорошо это делать.

Alen.Toma 10.02.2019 22:28

Рад слышать, что это помогло вам тогда. Но вы знаете, что использование jquary или vue значительно упрощает рендеринг данных. использование таблицы не лучше, вы не можете контролировать дизайн столько, сколько хотите. использование div или ul намного лучше, но тогда это то, что я думаю. И напоследок не забудьте проголосовать :)

Alen.Toma 10.02.2019 22:34

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

AdeDoyle 10.02.2019 23:18

Другие вопросы по теме