Предпросмотр картинки с открытием оригинала на JavaScript

При создании фотогалереи или любой системы, содержащей картинки, сталкиваешься с проблемой показа превьюшки, т.е. уменьшенной копии изображения с возможностью открыть оригинал. Как правило, для этого используют PHP-скрипт преобразования на сервере.

Я пошел по другому пути.

На сервере хранятся исходное изображение и миниатюра.

В атрибут src элемента img прописывается URL миниатюры, а URL оригинала большой картинки – в атрибут data-src.

<img src="images/thumbnail.jpg" data-src="images/image.jpg" alt="Портрет">

Портрет

Вся страница должна быть помещена в один внешний div для формирования затемненной подложки.

При клике на элемент img, содержащий атрибут data-src, срабатывает соответствующая функция.

Исходный код примера:

<script>
        function openwind(o){
            var a;
            if(o.nodeName=='IMG'){a=o.alt; fil=o.getAttribute('data-src');}
            else if(o.nodeName=='A'){a=o.title;fil=o.href;}
            else {fil=o;log("fil.nodeName=",fil.nodeName);}
            a="<img onclick='fb_close()' src='"+fil+"' onload='fb_resize(this);' onerror='fb_error(this)' style='visibility:hidden' alt='"+a+"'/>";
            fb_win(a);
            return false;
        }
        
        function fb_win(html,ev){
            fb_modal=document.createElement('div');
            fb_modal.setAttribute('class','fb-win');
            //console.log("fb_win:",html);
            fb_modal.innerHTML=((ev==2)?'':'<div class="fb-win0" onclick="fb_close()"></div>')+'<div class="fb-win1">'+
                '<a onclick="fb_close();return false;" title="Закрыть" href=""></a>'+html+
                '<div class="dragbar" onmousedown="DD.start(event)" onmouseup="DD.stop(event)" oncontextmenu="return false" ondragstart="return false" ondblclick="DD.full(event)" ondragend="return DD.stop(event)"></div></div>';
            document.body.appendChild(fb_modal);
            if(ev){
                if(ev==1){var d=document.forms;d=d[d.length-1][0].focus();}
                else if(ev==2){_fade.init();}
                else {/*console.log("eval:",ev); eval(ev);*/ExecScript(ev);}
            }
            addEvent(document, "keydown", fb_close);
            addEvent(window, "resize", fb_ResizeDocument);
            fb_ResizeDocument(); // для корректного расчета ширины окна нужно на все загружаемые в модальном окне изображения добавить onload="fb_ResizeDocument(event)"
            // если в окно загружаются элементы с неограниченной шириной - окно будет максимально широким
            var o=document.body.firstElementChild;
            if(o){ // фиксирую основной экран(первый div внутри body) как подложку
                var y = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
                var c=window.getComputedStyle(o, null);
                var b=window.getComputedStyle(document.body, null);
                //console.log(c.width, b.width,wW(),o);
                if(c.width!=b.width && !o.getAttribute('data-width'))o.setAttribute('data-width',c.width);
                o.style.width=Math.min(parseInt(c.width),wW())+'px'; // возможно была auto
                var l=parseInt((wW()-parseInt(parseInt(c.width)))/2); if(l<=10)l=0; // это вертикальная прокрутка
                //console.log('fb_win l=',l);
                o.style.left=l+'px';
                o.style.marginTop=-y+'px'; // подложка
                addClass(o,'fb-fix');
                window.scroll(0,0);
            }
            return fb_modal;
        }

        function fb_close(e) {// вызывается при нажатии клавиши в модальном окне и при нажатии на крестик
            if (f_reload)reload();
            if (typeof(e) == 'object') {
                e = e || window.event;
                if (e.type == 'keydown') {
                    if (e.keyCode == 27)fb_close(); // Esc
                    return;
                }
            }
            //var a=getElementsByClass('fb-win',null,'DIV');
            var a=document.querySelectorAll('div.fb-win');
            if (a&&a.length) {
                removeID(a[a.length - 1]);
                if (a.length < 2)removeEvent(document, "keydown", fb_close);
                a = getObj('Calendar');
                if (a && a.style)a.style.display = 'none'; // прячу календарь
                var o = document.body.firstElementChild;
                if (o) {
                    var c = window.getComputedStyle(o, null);
                    var y = -parseInt(c.marginTop);
                    removeClass(o, 'fb-fix');
                    o.style.margin = ''; // 0 auto
                    o.style.left = ''; // auto
                    //o.style.width='auto'; // возможно была auto
                    o.style.width = (c = o.getAttribute('data-width') ? c : ''); // auto
                    //console.log("body.width:",o.style.width);
                    window.scroll(0, y);
                }
            }else console.log('fb_close: Нет ни одного открытого окна!');
        }
        
        function fb_ResizeDocument(e){
            // вычисляю ширину модального окна и его положение по горизонтали
            var o=fb_modal.querySelector('DIV.fb-win1'); if(!o){console.warn('Нет модального окна');return;}
            var c=window.getComputedStyle(o, null);
            //console.log('до',o,'width=',c.width, wW(), e);
            if(!e || e.type!='resize' || wW()< parseInt(c.width)){
                o.style.width='auto';
                o.style.maxWidth=(wW()-10)+'px';
                //console.log('~',o.style.width,o.style.maxWidth,c.width);
                if(parseInt(c.width)<wW()&&wW()<500)o.style.width=wW()+'px';
                else o.style.width=parseInt(parseInt(c.width)>500 ? Math.min(wW(),parseInt(c.width)+20) : 500 )+'px';
                o.style.maxWidth='none';
                //console.log('Меняю',o);
            }else{
                //console.log('НЕ Меняю',e.type);
            }
            var db=o.querySelector('DIV.dragbar'); if(db)db.style.width=(parseInt(c.width)-45)+'px'; else console.warn('В модальном окне нет dragbar!');
            o.style.left=Math.max(0,parseInt((wW()-parseInt(parseInt(c.width)))/2))+'px';
            //console.log('после',c.width, o.style.left);
            // вычисляю положение модального окна по вертикали
            o.style.top=(parseInt(c.height)>wH()*0.99 ? '0' : Math.floor((wH()-parseInt(c.height))/2)+'px' );
            //console.log('fb_ResizeDocument', o.style.top );

        }

    </script>

.