skip to Main Content

I’m editing a Tampermonkey script that takes elements from the DOM and makes a new element from those to add them to a different part of the page. The original element has an X button that changes the display value of the div (to style="display: none")

<div id="alertTable">
    <ul>
        <li>Element 0 index 0
        <img src="example.com/img0.jpg">
        <img class="x-close" src="example.com/x_icon.png">
        </li>
    </ul>
</div>
$(document).ready(function newDiv() {
var newElementText = document.querySelector('#alertTable li');
var newElementImg = document.querySelector('#alertTable li img');
var newElementClose = document.querySelector('.x-close');

var newElementCSS = `
<style>
#scriptDiv img {
    width: 500px;
}
#scriptDiv li {
    color: #f8f8f8;
} 
</style>
`;

const HTMLElement = `
<div id="scriptDiv">
${newElementText}
${newElementImg}
${newElementClose}
</div>
`;

$('.DOMheader').prepend(HTMLElement);
});

The issue with that code is that sometimes the <ul> element of the DOM contains more than one element, like so, and the original code only detects and interacts with the first instance of the elements it finds with querySelector (logically)

<div id="alertTable">
    <ul>
        <li>Element 0 index 0
        <img src="example.com/img0.jpg">
        <img class="x-close" src="example.com/x_icon.png">
        </li>
        <li>Element 1 index 1
        <img src="example.com/img1.jpg">
        <img class="x-close" src="example.com/x_icon.png">
        </li>
        <li>Element 2 index 2
        <img src="example.com/img2.jpg">
        <img class="x-close" src="example.com/x_icon.png">
        </li>
    </ul>
</div>

I’d like to modify the original code to listen for the click on the .x-close element, and display the next <li> element’s information on the newElement variables. How could this be achieved? The original code includes this to "close" the DOM’s div when clicking on the injected element’s close button

var DOMClose = document.querySelector('#alertTable.x-close');
var ScriptClose = document.querySelector('#scriptDiv.x-click');

ScriptClose.addEventListener('click', function () {
 DOMClose.click();
}

2

Answers


  1. Chosen as BEST ANSWER

    Ended up going with the following solution. The divs to be closed (and the divs they take information from) are just alerts, they are meant to be closed in the website in question. The following code keeps track of the index number of the li node, and will both remove the script-generated node and render the next li node if one is present when a specific part of the site is interacted with. The specific node can then be appended (and styled) to whatever part of the page it's told to.

    // Declare the initial index number and make an array from all <li> elements within #alertTable
    var index = 0;
    var arr = Array.from(document.querySelectorAll('#alertTable li'));
    
    // Display the new HTML element, with a bit of a wait to make sure all elements have loaded before running
    function runInsert () { setTimeout(() => { insertElementFromArray(); }, 1000); }
    
    // Create the new HTML element and insert it
    function insertElementFromArray() {
    
      // The new HTML we will add to the site, including its own style
      const elementHTML = `
    <div id="newElement">
      <style>
      #newElement {
        width: 300px;
        height: 100%;
        background: #e7fe87;
      }
    
      #newElement img {
        width: 30px;
        height: 30px;
        margin: 10px;
      }
    
      #newElement .x-close {
        float: right;
        background: url(https://cdn-icons-png.flaticon.com/512/1828/1828843.png);
        background-size: 100%;
        width: 40px;
        height: 40px;
        top: 5px;
        right: 5px;
        margin: 10px;
        cursor: pointer;
      }
      </style>
      ${arr[index].innerHTML}
    </div>
    `;
      $('#upperDiv').append(elementHTML);
      
      // Listen for the click on the '#newElement x-close' element, and remove the elementHTML div. Move to the next element in the index, and render the next one if there is one.
      document.querySelector('#newElement .x-close').addEventListener('click', function () {
            $('#newElement').remove();
            index = index + 1;
            (arr[index] !== undefined) && (runInsert());
        });
    }
    
    runInsert();
    #upperDiv {
      position: fixed;
      top: 0;
      width: 100%;
      height: 85px;
      background: #000;
      background-size: auto 100%;
    }
    
    #alertTable {
      display: block;
      width: 200px;
      padding: 10px;
      overflow: auto;
      position: fixed;
      top: 90px;
      box-sizing: border-box;
      background-color: #4c4c4c
    }
    
    #alertTable ul {
      list-style: none;
      padding: 0;
      margin: auto;
      width: 100%;
    }
    
    #alertTable ul li {
      box-sizing: border-box;
      border-radius: 5px;
      width: 100%;
      height: auto;
      padding: 10px;
      margin-bottom: 15px;
      min-height: 85px;
      position: relative;
      background-color: #fff;
    }
    
    #alertTable img {
      width: 75px;
      height: 75px;
    }
    
    #alertTable .x-close {
      float: left;
      background: url(https://cdn-icons-png.flaticon.com/512/1828/1828843.png);
      background-size: 100%;
      width: 30px;
      height: 30px;
      top: 5px;
      right: 5px;
      cursor: pointer;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <body>
    <div id="upperDiv"></div>
    <div id="alertTable" style="visibility: hidden;">
        <ul>
            <li>
              <img src="https://cdn-icons-png.flaticon.com/512/4213/4213689.png">
              <div class="x-close" src="example.com/x_icon.png"></div>
              Element 0 index 0
            </li>
            <li>
              <img src="https://cdn-icons-png.flaticon.com/512/4213/4213633.png">
              <div class="x-close" src="example.com/x_icon.png"></div>
              Element 1 index 1
            </li>
            <li>
              <img src="https://cdn-icons-png.flaticon.com/512/4213/4213663.png">
              <div class="x-close"></div>
              Element 2 index 2
            </li>
        </ul>
    </div>
    </body>


  2. In the OP code the function newDiv() makes very little sense and doesn’t look like it has much to do with building a <ul> within a <div id="alertTable">. So assuming that this <ul> already exists you can register the <ul> to listen for any "click" events that trigger on itself and any elements within it (ie children elements). Then only define what happens when a particular element is clicked by the user and ignore the other elements (see Event Delegation).

    Clicking an item closed and opening the next item sounds like bad UX. How would the user reopen a closed item? In the example below, the user can toggle any item to open or close. If you prefer to have only a single item open at a time, refer to the comment labeled with a in the JavaScript portion of the example.

    Details are commented in example.

    // Reference the <ul>
    const list = document.querySelector('#alertTable ul');
    
    // Register <ul> to the "click" event...
    list.addEventListener("click", function(e) {
      /**
       * e.target is the element the user clicked.
       */
      // Reference the icon paths
      const oPNG = "https://i.ibb.co/vcpdyvK/open-white.png";
      const xPNG = "https://i.ibb.co/dbLgV41/x-square-white.png"
      // Reference the <li> that contains e.target
      const li = e.target.parentElement;
      // If e.target has class of "x-close"...
      if (e.target.matches(".x-close")) {
        // change e.target class to "x-open"...
        e.target.className = "x-open";
        // change e.target src...
        e.target.src = oPNG;
        // remove any class from the <li> that contains e.target.
        li.className = "";
      // Otherwise if e.target has class of "x-open"...
      } else if (e.target.matches(".x-open")) {
        /** ✤
         * Uncomment the next block if you want only a single <img>
         * to be shown at a time.
         */
        /* Remove this line to uncomment
        document.querySelectorAll("li").forEach(li => {
          li.className = "";
          li.lastElementChild.className = "x-open";
          li.lastElementChild.src = oPNG;
        });
        Remove this line to uncomment */
        // change e.target class to "x-close"...
        e.target.className = "x-close";
        // change e.target src...
        e.target.src = xPNG;
        // add class "active" to the <li> that contains e.target.
        li.className = "active";
      } else {
        // Otherwise if e.target is any other element end function
        return false;
      }
    });
    /**
     * The following styles can be added to newElementCSS
     */
    :root {
      font: 16px/1 "Segoe UI";
      overflow-y: scroll;
    }
    
    #alertTable {
      padding: 3px;
      background: black;
    }
    
    #alertTable li {
      display: flex;
      justify-content: space-between;
      margin: 3px 6px;
      padding: 3px;
      color: #f8f8f8;
    }
    
    /**
     * For each first <img> in a <li>, 
     * hide it by making it's height 0
     */
    #alertTable li img:first-of-type {
      width: 200px;
      height: 0;
      /* This animates the height when it goes to/from 0 */
      transition: height 0.5s ease-out;
    }
    
    /**
     * If a <li> has class "active",
     * the first <img> height becomes 200px
     */
    #alertTable li.active img:first-of-type {
      height: 200px;
    }
    
    #alertTable .x-close,
    #alertTable .x-open {
      width: 18px;
      height: 18px;
      cursor: pointer;
    }
    
    #alertTable .x-close {
      width: 20px;
    }
    <div id="alertTable">
      <ul>
        <li>Matrix
          <img src="https://i.ibb.co/c1PtcM7/matrix1.gif">
          <img class="x-open" src="https://i.ibb.co/vcpdyvK/open-white.png">
        </li>
        <li>Static
          <img src="https://i.ibb.co/bBGX3Sq/static.gif">
          <img class="x-open" src="https://i.ibb.co/vcpdyvK/open-white.png">
        </li>
        <li>Stars
          <img src="https://i.ibb.co/FHRS8Gx/stars.gif">
          <img class="x-open" src="https://i.ibb.co/vcpdyvK/open-white.png">
        </li>
      </ul>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search