How can you manage CSS selectors for identical code included multiple times in the same page?

I frequently create web apps that use traditional tabs at the top of the page. When doing this, I have a common problem I have been unable to find an elegant solution to.

The problem is the user can load the same content in two separate tabs on the same page. Any HTML ID’s on these two different tabs then conflict, causing any JavaScript referencing one of those ID’s to fail on the second tab. In addition, any JavaScript referencing a class on one of these pages will affect elements on all tabs, when I really only want it to affect the current tab.

For example, what if I included this block of code twice in the same page?

<style>
    #container { margin; 20px; }
    #message { background: red; }
</style>

<div id='container'>
    <span id='message'>This was from an include file</span>
    <button id='changeColorBtn'>Change color</button>
</div>

<script>
    $('#changeColorBtn').click(() => $('#message').css('background', 'blue'))
</script>

JSFIDDLE to illustrate the problem: https://jsfiddle.net/dillydadally/2njcq9py/

I have tried or considered three approaches to solve this in the past:

  1. I tried appending a page id to every element in the included content using PHP. This become messy quick and annoying to write out.

  2. I tried making each tab an iframe. At first, this seemed to work well, but quickly became a management nightmare having that many iframes open on the same page that sometimes needed to communicate with each other and share data. I ran into multiple issues and decided not to attempt this approach again.

  3. I considered wrapping each instance of included content in a single element with a unique ID, but I decided I would run into similar issues as option 1 above. Every CSS selector would have to have that element with the ID first, yet again creating messy code and possibly slowing the page down with numerous multi-depth JQuery selectors. In addition, there would still be multiple elements with the same ID on the same page (although I’m not sure that would matter since every selector should have a parent element included).

Is there an element or approach created to address this problem already in HTML/CSS that I’m missing?

1 thought on “How can you manage CSS selectors for identical code included multiple times in the same page?”

  1. You must use a class instead of an ID on an element with the same styling used more than once that you wish to affect with the same css style rule in your document.

    For example:

    
    <div id="main"><!-- / The main ID is only used once in the document / -->
      <p class="par">
        Here is the first paragraph with the same style as the second
      </p>
      <p class="par">
        Here is the second paragraph with the same style as the first
      </p>
    </div>
    
    

    Now to your issue of affecting the same type of element used multiple times in your document…

    Because you have both the span (message) and button together as a pair under the same parent element, you can access the index of a loop within an event listener to identify the one being pressed using the nodelist being selected.

    I use vanilla JS querySelectorAll() on the node list. You must use classes for multiple elements of the same tag name.

    I targeted the elements parent class let message = document.querySelectorAll('.content span') and let btn= document.querySelectorAll('.content button'). Then you run the nodes through a forEach loop and add an eventlistener to the button element using the index in the forEach loop. Though this index is the index for the btn element, because these are paired up it will target the correct message element using the index from the btn.

    let btn = document.querySelectorAll('.container button');
    let message = document.querySelectorAll('.container span');
    
    btn.forEach((button, i) => {
      // in short.... 
      // each time the loop iterates the nodelist of btn, it assigns the keyword
      // `button` to that iteration of the elements nodelist, so when we click a specific
      // `button` it refers to the button being pressed at that time and not all of them
      button.addEventListener('click', (e) => {
        message[i].style.backgroundColor = 'blue';
      })
    })
    .container {
      margin;
      20px;
    }
    
    .container span {
      background: red;
    }
    <div id='tab1'>
    
      <div class='container'>
        <span>This was from an include file</span>
        <button>Change color</button>
      </div>
    
      <script>
      </script>
    
    </div>
    
    
    <div id='tab2'>
    
    
      <div class='container'>
        <span>This was from an include file</span>
        <button>Change color</button>
      </div>
    
    
    </div>

    Using Jquery: Use $.each to run the list through a loop and use $(this) on a click event and target the elements previous sibling which is the grouped pair within the parents span tag (message).

    let btn = $('.container button');
    let message = $('.container span');
    
    
    $.each((btn), function(){
      $(this).click(function(){
        $(this).prev().css("background-color", "blue")
      })
    })
    .container {
      margin;
      20px;
    }
    
    .container span {
      background: red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id='tab1'>
    
      <div class='container'>
        <span>This was from an include file</span>
        <button>Change color</button>
      </div>
    
      <script>
      </script>
    
    </div>
    
    
    <div id='tab2'>
    
    
      <div class='container'>
        <span>This was from an include file</span>
        <button>Change color</button>
      </div>
    
    
    </div>
    Reply

Leave a Comment