skip to Main Content

i’m pretty new to html/javascript, and i’m trying to develop a visual novel online, and i want to create a "click to advance" feature, so everytime you click a new piece of text appears, like for example higurashi and tsukihime. I can’t seem to create the feature because whenever i do an "onclick" function, it spits out all the text at once.

document.querySelector("html").addEventListener("mousedown", function One() {
  const para = document.createElement("span");
  const node = document.createTextNode("Z.");
  para.appendChild(node);

  const element = document.getElementById("div1");
  element.appendChild(para);

  return;

});

document.querySelector("html").addEventListener("mousedown",function Two() {
  const para = document.createElement("span");
  const node = document.createTextNode("A.");
  para.appendChild(node);

  const element = document.getElementById("div1");
  element.appendChild(para);
});
<div id="div1"></div>

I expected it to work in the way that when you click, it creates a set of text, and then when you click again, it makes another set of text. unfortunately, everytime you click, it just spits out all of the content. should i use an await/promise function? Do I need a handler? How do I set that up? How do you clear the text and make new text after you’re done with a scene?

3

Answers


  1. For accessibility reasons, you should provide your entire content in the HTML itself instead of dynamically creating it with JS.

    Then you hide all those sections with the exception of the first by default. After that, you can add a class to the next class with JS to make them visible again:

    const ReadMoreBtn = document.querySelector('button');
    
    ReadMoreBtn.addEventListener('click', function() {
      let hidden_section = document.querySelector('section:not(.d-block)');
      if (hidden_section != null) {
        hidden_section.classList.add('d-block');
      } else {
        ReadMoreBtn.disabled = true;
      }
    })
    section {
      display: none;
    }
    
    .d-block {
      display: block;
    }
    <section class="d-block">Section 1</section>
    <section>Section 2</section>
    <section>Section 3</section>
    <section>Section 4</section>
    <section>Section 5</section>
    
    <button>read more</button>
    Login or Signup to reply.
  2. First of all, while it’s not inherently wrong to use text nodes, in this scenario, they are unnecessarily complicating your code. Creating and appending text nodes for each piece of text makes the code way harder to manage.

    I would highly recommend starting with tutorials on html/js (W3Schools or any other) tutorial before working on a large project like a visual novel.

    To solve your particular problem, I propose this solution:

    <!DOCTYPE html>
    <html>
    <body>
    <div id="text">Click anywhere to advance the text...</div>
    <script>
        // Array of texts
        var texts = [
            "This is the first text.",
            "This is the second text.",
            "This is the third text.",
            "This is the final text."
        ];
    
        var index = 0;
        var textElement = document.getElementById('text');
    
        // Function to change text on click
        function changeText() {
            textElement.textContent = texts[index];
            if (index < texts.length - 1) {
                index++; // Increment index until the second last text
            }
        }
    
        // Event listener to detect clicks
        document.addEventListener('click', changeText);
    
        // Initial text display
        changeText();
    </script>
    </body>
    </html>

    We store the text values in an array, but you can use any other method to pass the values if you wish. The eventListener is then attached to the whole document, so you can click any part of the page to advance the text.

    You don’t want to use await/promise here, as asynchronous programming is used to keep your program responsive even if you have a long-running task running in the background.

    In fact, you use a "handler" the moment you add an event listener to your program. In this case, the event handler is the changeText function.

    Login or Signup to reply.
  3. You could first load your content, and then build the HTML from it.

    The HTML your construct can be pre-hidden. As your request more content, you can remove the .hidden class.

    The example below uses Faker (to generate the content) and Tailwind (for CSS presentation).

    const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
    
    // Load content from external source
    const content = Array.from({ length: randInt(3, 5) }, () => ({
      title: faker.lorem.words(randInt(3, 8)),
      paragraphs: faker.lorem.paragraphs(randInt(1, 5)).split('n').map(text => text.trim())
    }));
    
    document.querySelector('main').insertAdjacentHTML('beforeend', renderContent(content));
    document.querySelector('button').addEventListener('click', revealNextSection);
    
    function renderContent(content) {
      return `
        ${content.map(({ title, paragraphs }, index) => `
          <section class="${index > 0 ? 'hidden' : ''} mb-8">
            <h2 class="mb-2 text-xl font-bold capitalize">
              ${title}
            </h2>
            ${paragraphs.map(paragraph => `
              <p class="mb-2">
                ${paragraph}
              </p>
            `).join('')}
          </section>
        `).join('')}
        <button
          type="button"
          class="rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:bg-slate-200 disabled:text-slate-500"
        >
          ${getButtonText(content.length - 1)}
        </button>
      `
    }
    
    function getButtonText(sectionsRemaining) {
      if (!sectionsRemaining) return 'Continue reading...';
      return `Continue reading... [${sectionsRemaining} section(s)]`;
    }
    
    function revealNextSection(e) {
      let hidden = nextHiddenSection();
      if (hidden) hidden.classList.remove('hidden');
      if (!nextHiddenSection()) e.target.disabled = true; // Disable the button
      e.target.textContent = getButtonText(remainingSections());
    }
    
    function nextHiddenSection() {
      return document.querySelector('section.hidden')
    };
    
    function remainingSections() {
      return document.querySelectorAll('section.hidden').length;
    };
    .disabled:cursor-not-allowed:disabled {
      cursor: not-allowed;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/faker.min.js"></script>
    <script src="https://cdn.tailwindcss.com"></script>
    <main class="p-4 text-sm"></main>

    Note: The Faker API supports { min, max } arguments, but I could not get them to work, so I used a randInt function.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search