javascriptgetelementsbyclassname

GetElementsByClassName - can't write the function to use the returned array


I have posted about this already, but I think my question was poorly explained. I have multiple divs with class of "popupEdit".
I want to be able to target these using getElementsByClassName with the result being a popup with some input field. See the code below
I know that getElementsByClassName returns an array of all the elements with the class EditQuestion, but I have failed to write a function that works to use this array due to my lack of skill (I'm a newbie).

Could someone give me the solution so I have something I can study? Apologies for asking directly for the answer... I have tried numerous things without success.
Many thanks

window.onload = function() {
  document.getElementsByClassName("EditQuestion").onclick = function() {
    var overlay2 = document.getElementsByClassName("overlay2");
    var popupEdit = document.getElementsByClassName("popupEdit");
    overlay2.style.display = "block";
    popupEdit.style.display = "block";
  };

  document.getElementsByClassName("CloseBtn2").onclick = function() {
    var overlay2 = document.getElementsByClassName("overlay2");
    var popupEdit = document.getElementsByClassName("popupEdit");
    overlay2.style.display = "none";
    popupEdit.style.display = "none";
  };
};
button.EditQuestion {
  padding: 0;
  border: none;
  background: none;
  color: #A8A8A8;
  font-weight: bold;
}

button.CloseBtn2 {
  padding: 0;
  border: none;
  background: none;
  position: absolute;
  right: 10px;
  top: 5px;
}

.popupEdit {
  display: none;
  position: fixed;
  left: 40%;
  top: 30%;
  width: 600px;
  height: 150px;
  margin-top: -75px;
  margin-left: -150px;
  background: #FFFFFF;
  border: 1px solid #000;
  z-index: 100000;
}

.overlay2 {
  display: none;
  position: fixed;
  left: 0px;
  top: 0px;
  width: 100%;
  height: 100%;
  background: #000;
  opacity: 0.5;
  z-index: 99999;
}
<button class="EditQuestion">Edit</button>
<div class="overlay2"></div>
<div class="popupEdit">
  <h2>Edit Question, some input box here..</h2>
  <button class="CloseBtn2">Close</button>
</div>

Edited version - I have tried to use querySelectorAll as suggested by TJ Crowder. The querySelector works, but when I add in the for loop and change to querySelectorAll, it fails. Any suggestions?

window.onload = function () {
  document.querySelectorAll(".EditQuestion").onclick = function () {
    var overlay2 = document.querySelectorAll(".overlay2");
    var popupEdit = document.querySelectorAll(".popupEdit");
    var index;
    for (index = 0; index < overlay2.length; ++index) {
      overlay2[index].style.display = "none";
      popupEdit[index].style.display = "block";
    }
  };

  document.querySelectorAll(".CloseBtn2").onclick = function () {
    var overlay2 = document.querySelectorAll(".overlay2");
    var popupEdit = document.querySelectorAll(".popupEdit");
    var index;
    for (index = 0; index < overlay2.length; ++index) {
      overlay2[index].style.display = "none";
      popupEdit[index].style.display = "block";
    }
  };
};

Solution

  • getElementsByClassName (on the browsers where it exists) returns a list, not a single element. So this line and similar:

    overlay2.style.display = "none";
    

    ...fails, because the list doesn't have a style property.

    If you just want to handle the first match, you can grab it via [0]:

    overlay2[0].style.display = "none";
    

    (That will fail if there are no matches, though.) Or, since getElementsByClassName isn't as well-supported as querySelector, you might prefer:

    overlay2 = document.querySelector(".overlay2"); // Gives you the first match; note the dot
    overlay2.style.display = "none";
    

    Or if you want to loop through all of them, you need a loop:

    var index;
    for (index = 0; index < overlay2.length; ++index) {
        overlay2[index].style.display = "none";
    }
    

    To get the list for that loop, either use getElementsByClassName as you are currently (but it won't work on IE8), or use querySelectorAll (which will):

    overlay2 = document.querySelectorAll(".overlay2"); // Gives you a list
    

    Could you show me how you would incorporate this loop into the JS function.

    I don't think you want a loop; you just want to handle the specific overlay and popup related to the button, right?

    I'd probably change the HTML slightly so that each group has a group div or similar around it:

    <div class="question"><!-- Wrapper div for each question -->
        <button class="EditQuestion">Edit</button>
        <div class="overlay2" style="display: none"></div><!-- Note I've hidden ... -->
        <div class="popupEdit" style="display: none">     <!-- ...these by default -->
            <h2>Edit Question, some input box here..</h2>
            <button class="CloseBtn2">Close</button>
        </div> 
    </div>
    

    ...and use event delegation:

    var container = document.getElementById("questions");
    hookEvent(container, "click", function(event) {
        var button, group, overlay, display;
    
        // Find the button that was clicked, if any
        button = event.target;
        while (button && (
              button.tagName.toUpperCase() !== "BUTTON" ||
              !button.className.match(/\bEditQuestion\b|\bCloseBtn2\b/)
            )) {
            button = button.parentNode;
        }
        if (button) {
            // One of our desired buttons was clicked, find the parent
            group = button.parentNode;
            while (group && !group.className.match(/\bquestion\b/)) {
                group = group.parentNode;
            }
            if (group) {
                overlay = group.querySelector(".overlay2");
                display = overlay.style.display === "block" ? "none" : "block";
                overlay.style.display = display;
                group.querySelector(".popupEdit").style.display = display;
            }
        }
    });
    

    ...where hookEvent looks something like this:

    function hookEvent(element, eventName, handler) {
        if (element.addEventListener) {
            element.addEventListener(eventName, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + eventName, function(event) {
                var e = event || window.event;
                if (!e.target) {
                    e.target = e.srcElement;
                }
                handler.call(element, e);
            });
        } else {
            throw "addEventListener or attachEvent required";
        }
    }
    

    Live Example | Source

    The great thing about event delegation is that since you're handling the event on a container, it doesn't matter how much you add or remove questions in the container, it just keeps working.

    A lot of the code above is to deal with IE weirdness, and to handle the event delegation. FWIW, a good DOM library can make that all a LOT simpler for you. Here's a jQuery example:

    $("selector for the container").on("click", ".EditButton, .CloseBtn2", function() {
        var button = $(this);
        button.closest('.question').find(".overlay2, .popupEdit").toggle(button.is(".EditButton"));
    });