Home > Blockchain >  Switching divs without using IDs in native JavaScript
Switching divs without using IDs in native JavaScript

Time:10-26

I'm not pro in JavaScript, I'm still learning. On my site, there is a hero section that has a title, paragraph, and is filled with a picture.

<div  style="background-image: url('https://source.unsplash.com/random/400x400');">

  <div >Main Text 1</div>
  <div >Main Text 2</div>
  <div >Main Text 3</div>

  <div >Paragraph text 1</div>
  <div >Paragraph text 2</div>
  <div >Paragraph text 3</div>

</div>

<div >
  <div >
    <img src="https://www.svgrepo.com/show/331860/dot.svg">
  </div>
  <div >
    <img src="https://www.svgrepo.com/show/331860/dot.svg">
  </div>
  <div >
    <img src="https://www.svgrepo.com/show/331860/dot.svg">
  </div>
</div>

I would like to switch divs with 3 "bullet" buttons - each is responsible for showing relevant content (1st bullet = 1st div with Main Text1, Paragraph Text1 and 1st url background, 2nd bullet = 2nd div with Main text 2 and so on).

I know that I can assign specific IDs to each "bullet" button and add event listener but I feel that way is not too good.

Thanks for help!

I'm looking for solution in pure JS.

I have tried the forEach function but i don't know exactly how it is supposed to work.

CodePudding user response:

You can get the child index of the .hero-switch within its parent. With this index, you can loop over all the titles and leads and toggle the display: none style rule.

Note: It is not necessary, but you should group all the titles and leads, or group each pair of title and lead. This adds some organization to the markup.

const getChildIndex = (el) => [...el.parentNode.children].indexOf(el);

const handleNavigation = (e) => {
  const index = getChildIndex(e.target.closest('.hero-switch'));
  const titles = document.querySelectorAll('.slider-title');
  const leads = document.querySelectorAll('.slider-lead');
  for (let i = 0; i < titles.length; i  ) {
    titles[i].classList.toggle('d-none', i !== index);
    leads[i].classList.toggle('d-none', i !== index);
  }
};

document.querySelectorAll('.hero-switch').forEach(hs =>
  hs.addEventListener('click', handleNavigation));
.slider {
  padding: 1rem;
  min-height: 8rem;
  background-color: #000;
  background-image: url('https://source.unsplash.com/random/400x400');
  background-size: cover;
}

.slider-title,
.slider-lead {
  text-shadow: 0.05rem 0.05rem 0.1rem #FFF;
}

.slider-title {
  font-size: 1.5rem;
  font-weight: bold;
}

.hero-switch:hover {
  cursor: pointer;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<div >
  <div >
    <div>
      <div >Main Text 1</div>
      <div >Main Text 2</div>
      <div >Main Text 3</div>
    </div>
    <div>
      <div >Paragraph text 1</div>
      <div >Paragraph text 2</div>
      <div >Paragraph text 3</div>
    </div>
    <div >
      <div ><img src="https://www.svgrepo.com/show/331860/dot.svg"></div>
      <div ><img src="https://www.svgrepo.com/show/331860/dot.svg"></div>
      <div ><img src="https://www.svgrepo.com/show/331860/dot.svg"></div>
    </div>
  </div>
</div>

CodePudding user response:

Regarding my earlier above comment ...

"Though an approach which structurally separates the navigation from the contents is ok, structurally separating content which belongs together in my opinion is not. Why not combine/collect content of a certain context/subject into containers, one container per context/subject? "

... how about an approach then which incorporates following ...

  • does display all content correctly without JavaScript enabled for it uses markup which groups related content.

  • does use semantic markup including a radio-list for the selectable content options (which makes it keyboard navigable).

  • uses kind of a component based approach where script initialization does not depend on class names but on component specific data-* attributes.

The implementation is straightforward

  1. two node lists are queried

    • one for the content options (the OP's bullet list)
    • one for the to be presented contents.
  2. each selection node gets assigned a single change handler where the latter was created as a method with bound data (the two node lists).

  3. upon this bound data and the event object's currentTarget one can manage the correct states for both the to be displayed and hidden contents.

  4. there will be a default selection/display of the first content item.

  5. the component gets assigned an initialized class name which triggers the correct display mode for enabled JavaScript (until then all content is on display).

function handleContentSwitchFromBoundData({ currentTarget }) {
  const { selectorList, contentList } = this;
  const selectedIndex = selectorList
    .findIndex(selectorNode =>
      selectorNode === currentTarget
     );
  contentList
    .forEach(({ classList }) =>
      classList.remove('selected')
    );
  contentList[selectedIndex]
    .classList.add('selected');
}

function initializeContentSwitchComponent(rootNode) {
  const selectorList = [...rootNode
    .querySelectorAll('[data-content-selector]')];
  const contentList = [...rootNode
    .querySelectorAll('[data-content-item]')];

  const handleContentSwitch = handleContentSwitchFromBoundData
    .bind({ selectorList, contentList });

  selectorList
    .forEach(selectorNode =>
      selectorNode
        .addEventListener('change', handleContentSwitch)
    );

  // 1st item default selection
  selectorList[0].click();

  rootNode
    .classList
    .add('initialized');
}

function main() {
  document
    .querySelectorAll('[data-content-switch]')
    .forEach(initializeContentSwitchComponent);
}
main();
[data-content-switch] {
  position: relative;
  display: block;
  padding: 20px 20px 45px 20px;
  background: #666 url('https://source.unsplash.com/random/400x400');
  background-size: cover;
}

[data-content-switch] ol {
  list-style: none;
  margin: 0;
  padding: 0;
}
[data-content-switch] > ol {
  position: relative;
  text-shadow: 0.05rem 0.05rem 0.1rem #fff;
  text-align: center;
}

[data-content-switch] > nav {
  z-index: 1;
  position: absolute;
  bottom: 10px;
  width: 100%;
  margin-left: -20px;
  text-align: center;
}
[data-content-switch] > nav > ol {
  position: relative;
  display: inline-block;
}
[data-content-switch] > nav > ol li {
  display: inline-block;
}

[data-content-switch] [data-content-options] {
  display: none;
}
[data-content-switch].initialized [data-content-options] {
  display: unset;
}

[data-content-switch] [data-content-item] {
  -webkit-transition: opacity 1s ease-in-out;
  -moz-transition: opacity 1s ease-in-out;
  transition: opacity 1s ease-in-out;
}
[data-content-switch].initialized [data-content-item] {
  display: inline-block;
  position: absolute;
  left: -100%;
  opacity: 0;
}
[data-content-switch].initialized [data-content-item].selected {
  position: relative;
  left: auto;
  opacity: 1;
}
<article data-content-switch>
  <nav data-content-options>
    <ol>
      <li>
        <label>
          <input type="radio" name="hero" data-content-selector />
          <!-- <span > some label text </span> //-->
        </label>
      </li>
      <li>
        <label>
          <input type="radio" name="hero" data-content-selector />
          <!-- <span > some label text </span> //-->
        </label>
      </li>
      <li>
        <label>
          <input type="radio" name="hero" data-content-selector />
          <!-- <span > some label text </span> //-->
        </label>
      </li>
    </ol>
  </nav>

  <ol data-target-content>
    <li data-content-item>
      <p>Main Text 1</p>
      <p>Paragraph text 1</p>
    </li>
    <li data-content-item>
      <p>Main Text 2</p>
      <p>Paragraph text 2</p>
    </li>
    <li data-content-item>
      <p>Main Text 3</p>
      <p>Paragraph text 3</p>
    </li>
  </ol>
</article>

  • Related