考虑堆叠层上下文如何处理javascript键绑定?

时间:2015-11-11 12:54:14

标签: javascript html css dom keyboard

可以说我打开了<body> full modal。按 [ESC] 键可以关闭此模态。 在此full modal范围内,用户可以打开另一个较小的模态,也可以通过按 [ESC] 键关闭。你如何处理 [ESC] 键并关闭'更高'层,阻止按键传播并关闭正在收听按键的其他层?

我期待一个直接的答案,使用preventDefault或类似的东西..我不打算设置某种服务,在决定应该关闭哪一层之前进行很多检查..对我来说,这件事应该有点工作比如点击事件,向上传播。这可行吗?

3 个答案:

答案 0 :(得分:7)

没有直接的.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

答案 1 :(得分:1)

我们可以使用 Stack 数据结构来解决这个问题,该数据结构可用于维护创建和删除模态窗口的状态。

最顶层的模态将是堆栈的顶部/顶部,将在转义时首先删除。以下是如何实现这一点的简单演示。

最后,创建堆栈实现不需要额外的工作,JavaScript Array具有内置的pushpop方法,我们将在下面的实现中使用它们。

&#13;
&#13;
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's 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
    }
    
})();
&#13;
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;
}
&#13;
<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>
&#13;
&#13;
&#13;

由于我们可以控制所有模态窗口,我们可以根据新窗口的创建或删除来更改其他模态的不透明度或使父模式显示/隐藏,如下所示

&#13;
&#13;
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's 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(); 
              if(modalStack.length > 0) modalStack[modalStack.length - 1].style.display = 'block';
               //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);
        
        
      if(modalStack.length > 0) modalStack[modalStack.length - 1].style.display = 'none';
      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);
            if(modalStack.length > 0) modalStack[modalStack.length - 1].style.display = 'block';
		}
         });
    }
    
    return {
        createModal : createModal,
        initialize: initialize
    }
    
})();
&#13;
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;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 2 :(得分:1)

我认为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>

用o键打开,用esc键关闭。