Как обращаться с ключевыми словами javascript с учетом сложного контекста слоя?

Предположим, что у меня есть <body> с открытым full modal. Этот модаль можно закрыть нажатием клавиши [ESC]. Внутри этого full modal пользователь может открыть другой, более мелкий модальный, который также можно закрыть, нажав клавишу [ESC]. Как вы управляете клавишей [ESC] и закрываете "верхний" уровень, предотвращая распространение нажатия клавиши и закрытие других слоев, которые прослушивают нажатие клавиши?

Я ожидаю прямой ответ, используя preventDefault или что-то подобное. Я не собираюсь устанавливать какой-то сервис, который делает много проверок, прежде чем решить, какой слой должен быть закрыт. Для меня эта вещь должна работать любопытно как события кликов, распространяются вверх. Это выполнимо?

Ответ 1

Там нет прямой .preventDefault() или некоторой черной магии, которая мешает вам блокировать пузырьки по прослушивателю событий, если вы не собираетесь создавать новый обработчик каждый раз, когда хотите присоединить прослушиватель событий специально для этого модаля, который не мешает другие обработчики.

Вместо того, чтобы иметь массив обработчиков и множество прослушивателей событий, почему бы вам не складывать модалы внутри массива, а затем уничтожать их один за другим?

Это самый короткий код, о котором я могу думать, см. комментарии внутри кода для пошагового объяснения.

// Global variable, we use this to store DOM objects.
var modalStack = [];

// My function to create/open my modals.
function openModal() {
    var modalHTML = '<div id="modal-'+modalStack.length+'" class="modal"><button onclick="openModal()">Open modal '+(modalStack.length+1)+'</button></div>'; // I populate the modal with my stuff and assign it an ID containing the current stack size. 
    document.body.insertAdjacentHTML( 'beforeend', modalHTML ); // Add into the body
    modalStack.push( document.getElementById('modal-'+modalStack.length) ); // And push my DOM object I just created into the array.
}


// My ESC event listener
window.addEventListener('keyup', function(event) {
    var lastIndex = modalStack.length-1; // This gets the last element on the stack.
    if (event.keyCode == 27 && lastIndex >= 0) {
        var thisModal = modalStack[ lastIndex ]; // Just to make sense, I could've called the removeChild directly from the array.
        thisModal.parentNode.removeChild(thisModal); // Destroy the current element.
        modalStack.pop(); // Remove the associated last DOM object from the array.
    }
}, false);

jsFiddle Demo

Ответ 2

Мы можем решить эту проблему, используя Stack структуру данных, которая может использоваться для поддержания состояний создание и удаление модальных окон.

Самый верхний модальный будет Top/Head из стека, который сначала будет удален при побеге. Ниже приведена простая демонстрация того, как это можно реализовать.

Наконец, нет необходимости в дополнительных усилиях по созданию реализации стека, у JavaScript Array есть встроенный метод push и pop, и мы будем использовать те, которые представлены ниже.

var Modal = (function() {

    //Just to give different margins for each modal
    var BASE_MARGIN = 20;
    
    //STACK, in this array we will store all modals
    var modalStack = [];
    
    //Creates all DOM and attach it to document body
    function createModal() {
        var modal = document.createElement('div');
        modal.className = 'modal';
        modal.style.margin = ((modalStack.length + 1) * BASE_MARGIN) + 'px';
        
        var header = document.createElement('div');
        header.className = 'modalHeader';
        header.innerHTML = 'Level-' + (modalStack.length + 1);
        
        var close = document.createElement('div');
        close.className = 'closeModal';
        close.innerHTML = 'X';
        close.addEventListener("click", function() {
            var index = modalStack.indexOf(modal);
            for(var i = modalStack.length - 1; i >= index; i--) {
		var div = modalStack.pop(); 
               //no need of i, because pop will always get the last element
		document.body.removeChild(div);
            }
        });
        
        header.appendChild(close);
	   
        var createModalButton = document.createElement('button');
        createModalButton.className = 'createButton';
        createModalButton.addEventListener("click", createModal);
        createModalButton.innerHTML = "Create Modal";
        
	modal.appendChild(header);
        modal.appendChild(createModalButton);
        
        document.body.appendChild(modal);
        modalStack.push(modal);
    }
    
    /**
    * Should be called on dom-loaded
    */
    function initialize() {
         document.addEventListener("keyup", function(ev) {
		if (ev.keyCode == 27) { // escape key maps to keycode `27`
			var div = modalStack.pop();
			document.body.removeChild(div);
		}
         });
    }
    
    return {
        createModal : createModal,
        initialize: initialize
    }
    
})();
div.modal {
    position: fixed;
    top: 0px;
    bottom: 0px;
    left: 0px;
    right: 0px;
    margin:20px;
    z-index = 100;
    background-color: #fff;
    border: 2px solid #333;
}

button.createButton {
    display: block;
    margin: auto;
    margin-top: 4px;
}

.modalHeader {
    background-color: lightsteelblue;
    border-bottom: 1px solid #555;
    color: white;
    padding: 6px 6px 6px 24px;
}

.closeModal {
    color: red;
    cursor: pointer;
    display: inline-block;
    float: right;
    margin-right: 14px;
}
<html>
<head>
</head>
<body onload="Modal.initialize();">
   <!--    Initial button to create child modals -->
   <input style="margin:50px 200px" class="createButton" 
       type="button" onclick="Modal.createModal();" value="Create Modal" />
</body>
</html>

Ответ 3

Самый простой способ я могу представить pf, используя только JS:

function openModalOnTop(modalHtml)
{
  $modalsContainer = document.getElementById("modalscontainer");
  var modalContainerTemplate = document.createElement("div");
  modalContainerTemplate.className = "modal";
  modalContainerTemplate.innerHTML  = modalHtml;
  $modalsContainer.appendChild(modalContainerTemplate);
}

function closeTopModal()
{
  $modalsContainer = document.getElementById("modalscontainer");
  $modalsContainer.lastChild.remove();
}

window.addEventListener('keyup', function(event) {
    if (event.keyCode == 79) {
      console.log("open");
      openModalOnTop("someHTMLHere");
    }
}, false);


window.addEventListener('keyup', function(event) {
    if (event.keyCode == 27) {
      console.log("close");
      closeTopModal();
    }
}, false);
.modal {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: rgba(0,0,0,0.2);
}
<div id="modalscontainer"></div>