Search code examples
javascriptjsonscopesimplemodaltemplate-literals

How can I use declared variables of a function outside the function but keeping their values in javascript?


I have this array of objects

const GALLERY = [
{
    color: "red",
    titulo: "Stuff"
},
{
    color: "pink",
    titulo: "Thing"
},
{
    color: "green",
    titulo: "Something"
}
];

and these functions with template literals

function galTemplate(gal) {
    let modalBtn = gal.color + "-btn";
    return `
    <div class="content-entry" id="${modalBtn}">
        <div href="" style="background-color: ${gal.color};"></div>
        <p class="gallery-entry-titulo">${gal.titulo}</p>
    </div>
    `
}

function galModal(modal) {
    let modalModal = modal.color + "-modal";
    return `
    <div class="popup-bg" id="${modalModal}">
      <div class="entry-popup">
        <div style="background-color: ${modal.color};></div>
      </div>
    </div>
    `
}

The situation is that I want to use the variables modalBtn and modaModal with their values to create a simple modal function that looks like this

modalBtn.onclick = function() {
    modalModal.style.display = "block";
}

Of course, this is an example of the real project. This is the Codepen of the complete example for best comprehension


Solution

  • Variables

    You can declare variables outside the scope of a function with the keyword var like this:

    var my_global = 1;
    function do_something_with_global() {
        console.log(my_global++);
    }
    do_something_with_global(); // will print: 1
    console.log(my_global); // will print: 2
    

    They will live in the global space so you can access them from everywhere. This is generally a bad design choice because this stuff will not be garbage collected (in a function-scope the memory will be freed once it is executed).

    Besides, globals can be modified from everywhere now, so you don't want that either. There could be hard-to-spot bugs when you accidentially override the variables, too.

    Just be aware of this. You may also want to read this here: https://www.w3.org/wiki/JavaScript_best_practices

    Possible solution based on your aim

    I would probably choose a different way of creating the elements and rendering them in one go (i.e. createElement() and then add the listeners and modals in sequence).

    However, I wanted to change as few as possible so that you can work with it more easily.

    You could and probably should also just toggle a CSS class instead of modifying the individual attributes.

    How about the following:

    const GALLERY = [{
        color: 'red',
        titulo: 'Stuff'
      },
      {
        color: 'pink',
        titulo: 'Thing'
      },
      {
        color: 'green',
        titulo: 'Something'
      }
    ];
    
    function galTemplate(gal) {
      let modalBtn = gal.color + '-btn';
      return `
        <div class="content-entry" id="${modalBtn}">
            <div href="" style="background-color: ${gal.color};"></div>
            <p class="gallery-entry-titulo">${gal.titulo}</p>
        </div>
        `;
    }
    
    function galModal(modal) {
      let modalModal = modal.color + '-modal';
      return `
        <div class="popup-bg" id="${modalModal}">
          <div class="entry-popup">
            <div style="background-color: ${modal.color};"></div>
          </div>
        </div>
        `;
    }
    
    function addClickListeners(gal) {
      let btnId = gal.color + '-btn';
      let modalId = gal.color + '-modal';
      let elBtn = document.getElementById(btnId);
      let elModal = document.getElementById(modalId);
      elBtn.onclick = function() {
        console.log('click: btn: ' + this.id + ', modal: ' + elModal.id);
        elModal.classList.toggle('show-popup');
      }
      /* to dispose the popup on click */
      elModal.onclick = function() {
        this.classList.toggle('show-popup');
      }
      /* --- */
    }
    
    document.getElementById("my-content").innerHTML = `
      <div class="content">
        ${GALLERY.map(galTemplate).join('')}
      </div>
      ${GALLERY.map(galModal).join('')}
    `;
    GALLERY.map(addClickListeners);
    body {
      background: #01235f;
      color: white;
    }
    
    .content {
      display: grid;
      grid-template-columns: repeat(5, 1fr);
      grid-gap: 0px;
      grid-auto-rows: 1fr;
    }
    
    .content-entry {
      width: 150px;
      height: 150px;
      position: relative;
      cursor: pointer;
    }
    
    .content-entry div {
      width: 100%;
      height: 100%;
    }
    
    .gallery-entry-titulo {
      position: absolute;
      bottom: 10px;
      left: 50%;
      transform: translate(-50%, -30%);
      cursor: pointer;
    }
    
    .popup-bg {
      display: none;
      position: fixed;
      z-index: 3;
      left: 0;
      top: 0;
      bottom: 0;
      right: 0;
      width: 100%;
      height: 100%;
      background-color: rgb(0, 0, 0);
      background-color: rgba(0, 0, 0, 0.6);
      backdrop-filter: blur(5px);
    }
    
    .show-popup {
      display: block;
    }
    
    .entry-popup {
      position: absolute;
      z-index: 9;
      top: 0;
      left: 0;
      bottom: 0;
      right: 0;
      margin: auto;
      width: 50%;
      height: 80%;
      overflow: auto;
      background-color: rgba(0, 0, 0, 0.4);
      padding: 60px;
    }
    
    .entry-popup div {
      width: 100%;
      height: 100%;
      margin: 20px auto;
    }
    <div class="content" id="my-content"></div>