📜 ⬆️ ⬇️

Once again about modal windows and some goodies.

About modal windows it is written, probably, tons of literature, but this article was inspired me to write this topic in Habré. There are a lot of things left unsaid, including horizontal jumps of both the page and the modal window.
Of course, you could just unsubscribe in the comments, they say, do so-and-so, and everything will be type-top. But my comment has grown to the size of a new article, with good examples and comments.
To whom it became interesting - welcome under habrakat!

Frankly speaking, I cannot call this type of viewing content on the page a “modal window”. Still, these are slightly different things. Therefore, I will call it "cloud content." I think this is the most correct definition for this kind of content display.

So, our main tasks:
1) to manage to fix the page so that with a hot change in its height, it does not “jump” to the right-to-left.
2) open the cloud window and center it so that the content on the page remains in place when the height of the window changes.
3) achieve vertical scrolling of the “cloud window” using the “Up and Down Arrows” keys on the keyboard.
4) previously opened windows are cached in the DOM, in order not to perform complex operations and calculations or not to make an extra request to the server.
5) connect the hot navigation keys for the most convenient end-user navigation (keyboard arrows Right / Left to move between windows and the Escape key to close the window).

Items 4 and 5 and will be buns.
')
Everything else, we will not use any frameworks, and we will write on native JS. Looking ahead, I will say that the JS code without comments in uncompressed form takes only 5 kb.

Actually, let's start.
Immediately I will show a ready illustrative example, so that when reading this article you have something to compare and see what exactly is being said.

DEMO EXAMPLE and the file connected to the JS page (JS can open "beaten", so you can immediately download the entire archive ).

First, we define the function elemID (); Since the document.getElementById construction is found in the code quite often, it is easier to shorten this record using a similar function, which allows you to significantly reduce the overall weight of the JS file. This is especially important with massive files with a large amount of code.

function elemID(e) {return document.getElementById(e);}; 


We get an object with which we will work later:

 var Content = {} 


The first thing we start with is a page fix. To do this, we wrap all the content of the page in a block with id = "layer"

 <body id="body"> <div class="layer" id="layer"> <!--     --> </div> </body> 


Is done. Now for him we write a method that centers this block:
 editSiteLayer:function (content) { /**    ,    - "layer" **/ if (!content) {var content = 'layer';} /** **     **   18px -     **/ var inWidth = window.innerWidth-18, /**    **/ layerWidth = elemID(content).clientWidth; /**        undefined **/ if (inWidth && layerWidth) { /**      CSS  float:left **/ elemID(content).style.cssFloat = 'left'; /** **           -    **            **/ elemID(content).style.marginLeft = (inWidth/2)-(layerWidth/2)+'px'; } } 

They wrote and temporarily forgot about it (at the end we will definitely connect it).

Our second task is to create and open a “cloud window”.
We will open the cloud window like this:
 <body id="body"> <div class="cloud" id="cloud"> <div class="cloud-layer" id="cloud-layer"> <em class="cloud-contaner" id="cloudpage__ID_"> </em> </div> </div> </body> 

The “cloud” block acts as a kind of protective mask and separates the site content from the cloud window, while also making it “cloudy”. Its CSS properties are:
 .cloud{ display:block; padding:0; margin:0; position:fixed; top:0; left:0; right:0; bottom:0; background: rgba(0, 0, 0, 0.795); z-index:3; overflow:auto; } 

The “cloud-layer” block is the “parent” for all DOM elements with cloud content. It is fixed in the center of the page and is given the property position: relative; (it allows you to scroll the window with the keys of the keyboard).
Its CSS properties are:
 .cloud .cloud-layer{ position:relative; float:left; width:720px; margin:0; padding:0; z-index:4; } 

The “cloud-contaner” block is the container for our cloud content. Why in the EM tag? Subsequently, we will go over the DOM in search of the current block and in order to hide all the existing ones. Search on the tag for us is most welcome.

To understand the real problem - imagine that we want to view all the personal photos of the user on our website. Usually done this way: when you first click on a photo, an http-request to the server is made to get an array of all the photos, their unique IDs, descriptions and other information.
For example, I will not do this, but I pre-ordered this array and prepared it for work:
 var photoInfo = []; photoInfo[0] = {'id':5478,'name':'  №1','desc':'  №1','link':'1347690631.jpg'}; photoInfo[1] = {'id':4198,'name':'  №2','desc':'  №2','link':'1347691505.jpg'}; photoInfo[2] = {'id':7596,'name':'  №3','desc':'  №3','link':'1347691550.jpg'}; photoInfo[3] = {'id':98637,'name':'  №4','desc':'  №4','link':'1347691521.jpg'}; 


Now, when opening the window, we rely on the unique ID of the photo, iterate through the array, select from it the necessary information on the current photo, as well as the ID of the previous and next photo to navigate. At the same time, we go over the DOM and check if there is actual content in the DOM. If it exists, we open it; if not, we generate a new block and insert it into the cloud. But first we check if there is a block with a cloud in the DOM or not yet. If not yet, and this is the first discovery of a cloud — the generation of a cloud, and in it the generation of the parent of cloud content.

 /** *  cloudNav()     cloudShow() *         **/ _cloudNav:function (gid) { if (photoInfo) { var num, prev, next, info, len = photoInfo.length; if (len > 0) { for(var i=0; i<len; i++) { if (photoInfo[i].id == gid) { current = i; info = photoInfo[i]; if (len > 1 && (len-1) > i) {next = photoInfo[i+1].id;} if (len > 1 && i > 0) {prev = photoInfo[i-1].id;} num = i+1; } } } return {"len":len,"num":num,"prev":prev,"current":gid,"next":next,"info":info}; } }, /** *  cloudShow()   ,      * gid -      **/ cloudShow:function (gid) { /**   ,     **/ var arrNav = Content._cloudNav(gid); if (arrNav && arrNav.info) { /**       **/ if (arrNav.prev !== undefined) {ContentPointPrev = arrNav.prev;} else {ContentPointPrev = null;} if (arrNav.next !== undefined) {ContentPointNext = arrNav.next;} else {ContentPointNext = null;} /**   ,          -,    **/ if (elemID('body').style.overflowY != 'hidden') { elemID('body').style.overflowX = 'hidden'; elemID('body').style.overflowY = 'hidden'; } /**        -    **/ if (!elemID('cloud')) { /**      "cloud" **/ var box = document.createElement('div'); box.className = 'cloud'; box.id = 'cloud'; elemID('body').appendChild(box); /**    "cloud" -    **/ var cloudbox = document.createElement('div'); cloudbox.className = 'cloud-layer'; cloudbox.id = 'cloud-layer'; elemID('cloud').appendChild(cloudbox); /**    "cloud"     // **/ var navbox = document.createElement('div'); navbox.id = 'cloud-nav'; elemID('cloud').appendChild(navbox); /**       **/ Content.editSiteLayer('cloud-layer'); /**    -        EM    **/ } else { var ems = elemID('cloud').getElementsByTagName('EM') if (ems.length > 0) { for(var i=0; i<ems.length; i++) { ems[i].style.display = 'none'; } } } /**    **/ elemID('cloud-nav').innerHTML = ''; /**        -    **/ if (!elemID('cloudpage_'+gid)) { var contentbox = document.createElement('em'); contentbox.className = 'cloud-contaner'; contentbox.id = 'cloudpage_'+gid; elemID('cloud-layer').appendChild(contentbox); var html = '<div class="cloud-title">'+ '<div class="cloud-name">'+ ' '+arrNav.num+'  '+arrNav.len+ '</div>'+ '<div class="cloud-close" onclick="return Content.cloudClose();"> </div>'+ '</div>'+ '<div class="cloud-body">'+ '<p><img onclick="'+(arrNav.next !== undefined ? 'return Content.cloudShow('+arrNav.next+')' : 'return Content.cloudClose();')+'" src="images/'+arrNav.info.link+'" alt="" /></p>'+ '<div class="more-button" onclick="Content.Slide(this, {point:\'next\', hide:\'   \', show:\'   \'})">'+ '   '+ '</div>'+ '<span style="display:none;">'+ '<p>'+arrNav.info.name+'</p>'+ '<p>'+arrNav.info.desc+'</p>'+ '<p>'+lorem[0]+'</p>'+ '<p>'+lorem[1]+'</p>'+ '<p>'+lorem[0]+'</p>'+ '<p>'+lorem[1]+'</p>'+ '<p>'+lorem[0]+'</p>'+ '<p>'+lorem[1]+'</p>'+ '</span>'+ '</div>'; elemID('cloudpage_'+gid).innerHTML = html; /**        **/ } else { elemID('cloudpage_'+gid).style.display = 'block'; } /**    **/ var navi = '<div class="cloudnavclose" onclick="return Content.cloudClose();"></div>'; if (arrNav.prev !== undefined) {navi+= '<div onclick="return Content.cloudShow('+arrNav.prev+')" class="cloudnavprev"></div>';} if (arrNav.next !== undefined) {navi+= '<div onclick="return Content.cloudShow('+arrNav.next+')" class="cloudnavnext"></div>';} elemID('cloud-nav').innerHTML = navi; /**  ,    **/ ContentShow = true; /**     **/ elemID('cloud').style.display = 'block'; } }, 

Above, I used some values ​​of the “lorem” array - in it I prepared in advance a very long text to visually lengthen the page in order to show that the window is fixed.

When the window is opened, the Content.editSiteLayer ('cloud-layer') method is closed; - by the previously described method, we center the cloud window on the center of the page, without letting it jump to right-left when a scroll bar appears.

When closing the window, the Content.cloudClose () method is used; Here he is:
 /**  cloudClose()    **/ cloudClose:function () { /**      **/ elemID('cloud').style.display = 'none'; /**        **/ elemID('body').style.overflowX = 'auto'; elemID('body').style.overflowY = 'auto'; /**      **/ ContentShow = null; } 


And our last task is to connect hotkeys. To do this, we will create the Content.init () method, which will process the hot keys and, when the page loads, immediately center it in the browser window (this is what I wrote at the very beginning of the article):
 /** *  init()         **/ init:function () { /**      **/ Content.editSiteLayer('layer'); /**      **/ window.onresize = function() { /**      **/ Content.editSiteLayer('layer'); /**   ,    **/ if (ContentShow) { Content.editSiteLayer('cloud-layer'); } }; /**      **/ window.onkeydown = function(event) { /**     **/ if (ContentShow) { /**   **/ event = event || window.event; switch(event.keyCode) { case 27: //  "Escape" Content.cloudClose(); break; case 37: //  " " (    ID   ) if (ContentPointPrev !== null){Content.cloudShow(ContentPointPrev);} break; case 39: //  " " (    ID   ) if (ContentPointNext !== null){Content.cloudShow(ContentPointNext);} break; } } }; } 

This method must be written at the very end of the page before closing the <BODY> tag:
 <script type="text/javascript" language="javascript">Content.init();</script> 


Some will ask why this is the case, and why it cannot be hanged on the body tag in the form
<BODY onload = "Content.init ();"> or use the window.onload () construct. But in this case our object will not start working until the whole page is loaded to the end. If you have a lot of advertising on the page, this is all the more unpleasant. Yes, and a sharp jump of the page to the left by 9px when centering it will be a little annoying.

In general, we coped with the main tasks. Once again, DEMO EXAMPLE .

It was tested in browsers Opera 12.00, Chrome 21 and higher, IE 8 and 9, Safari 5.0 and Yandex.browser 1.0
Unfortunately, there is no way to test it in Firefox.

Thank you for reading the article to the end.
You can download all the CSS, JavaScript files and html from the demo example in this archive .
The archive contains 2 JS files:
page.js - file with detailed comments
js.js - the same file, but without comments

I am pleased to answer all your questions, as well as listen to any criticism.

Source: https://habr.com/ru/post/153073/


All Articles