打开模态窗口在背景中移动绝对/固定定位元素

时间:2014-06-19 07:36:59

标签: jquery background modal-window

我使用jQuery Popup Overlay(http://www.jqueryscript.net/demo/Responsive-Accessible-jQuery-Modal-Plugin-Popup-Overlay/)作为可滚动模式窗口,当模态窗口打开时,原始滚动条会消失。这使得背景体略微向右跳跃。通常,脚本通过向主体添加边距权限来调整此值。但是这个解决方案似乎不适用于绝对或固定的元素。

不删除原始滚动条不是一个选项,因为我需要一个新的滚动条用于模态窗口,否则会有两个滚动条。

我还尝试手动向固定/绝对定位元素添加margin-right,但这似乎没有任何效果,添加padding-right确实有效。然后我试图改变每个边缘权利'通过' padding-right'在脚本中希望它可以工作,但它没有正常工作:背景仍然移动,当模态窗口再次关闭时,滚动条没有回来。因此,将margin-right更改为padding-right可能会有效但我更改了脚本错误(我对JQuery一无所知,我只是更改了单词)。

这是剧本:

var $window = $(window);
var options = {};
var zindexvalues = [];
var lastclicked = [];
var scrollbarwidth;
var bodymarginright = null;
var opensuffix = '_open';
var closesuffix = '_close';
var stack = [];
var transitionsupport = null;
var opentimer;

var methods = {

    _init: function (el) {
        var $el = $(el);
        var options = $el.data('popupoptions');
        lastclicked[el.id] = false;
        zindexvalues[el.id] = 0;

        if (!$el.data('popup-initialized')) {
            $el.attr('data-popup-initialized', 'true');
            methods._initonce(el);
        }

        if (options.autoopen) {
            setTimeout(function() {
                methods.show(el, 0);
            }, 0);
        }
    },

    _initonce: function (el) {
        var $el = $(el);
        var $body = $('body');
        var $wrapper;
        var options = $el.data('popupoptions');
        var css;

        bodymarginright = parseInt($body.css('margin-right'), 10);
        transitionsupport = document.body.style.webkitTransition !== undefined ||
                            document.body.style.MozTransition !== undefined ||
                            document.body.style.msTransition !== undefined ||
                            document.body.style.OTransition !== undefined ||
                            document.body.style.transition !== undefined;

        if (options.type == 'tooltip') {
            options.background = false;
            options.scrolllock = false;
        }

        if (options.backgroundactive) {
            options.background = false;
            options.blur = false;
            options.scrolllock = false;
        }

        if (options.scrolllock) {
            // Calculate the browser's scrollbar width dynamically
            var parent;
            var child;
            if (typeof scrollbarwidth === 'undefined') {
                parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body');
                child = parent.children();
                scrollbarwidth = child.innerWidth() - child.height(99).innerWidth();
                parent.remove();
            }
        }

        if (!$el.attr('id')) {
            $el.attr('id', 'j-popup-' + parseInt((Math.random() * 100000000), 10));
        }

        $el.addClass('popup_content');

        $body.prepend(el);

        $el.wrap('<div id="' + el.id + '_wrapper" class="popup_wrapper" />');

        $wrapper = $('#' + el.id + '_wrapper');

        $wrapper.css({
            opacity: 0,
            visibility: 'hidden',
            position: 'absolute'
        });

        if (options.type == 'overlay') {
            $wrapper.css({overflow: 'auto'});
        }

        $el.css({
            opacity: 0,
            visibility: 'hidden',
            display: 'inline-block'
        });

        if (options.setzindex && !options.autozindex) {
            $wrapper.css('z-index', '100001');
        }

        if (!options.outline) {
            $el.css('outline', 'none');
        }

        if (options.transition) {
            $el.css('transition', options.transition);
            $wrapper.css('transition', options.transition);
        }

        // Hide popup content from screen readers initially
        $el.attr('aria-hidden', true);

        if ((options.background) && (!$('#' + el.id + '_background').length)) {

            $body.prepend('<div id="' + el.id + '_background" class="popup_background"></div>');

            var $background = $('#' + el.id + '_background');

            $background.css({
                opacity: 0,
                visibility: 'hidden',
                backgroundColor: options.color,
                position: 'fixed',
                top: 0,
                right: 0,
                bottom: 0,
                left: 0
            });

            if (options.setzindex && !options.autozindex) {
                $background.css('z-index', '100000');
            }

            if (options.transition) {
                $background.css('transition', options.transition);
            }
        }

        if (options.type == 'overlay') {
            $el.css({
                textAlign: 'left',
                position: 'relative',
                verticalAlign: 'middle'
            });

            css = {
                position: 'fixed',
                top: 0,
                right: 0,
                left: 0,
                bottom: 0,
                textAlign: 'center'
            };

            if(options.backgroundactive){
                css.position = 'relative';
                css.height = '0';
                css.overflow = 'visible';
            }

            $wrapper.css(css);

            // CSS vertical align helper
            $wrapper.append('<div class="popup_align" />');

            $('.popup_align').css({
                display: 'inline-block',
                verticalAlign: 'middle',
                height: '100%'
            });
        }

        // Add WAI ARIA role to announce dialog to screen readers
        $el.attr('role', 'dialog');

        var openelement =  (options.openelement) ? options.openelement : ('.' + el.id + opensuffix);

        $(openelement).each(function (i, item) {
            $(item).attr('data-popup-ordinal', i);

            if (!item.id) {
                $(item).attr('id', 'open_' + parseInt((Math.random() * 100000000), 10));
            }
        });

        // Set aria-labelledby (if aria-label or aria-labelledby is not set in html)
        if (!($el.attr('aria-labelledby') || $el.attr('aria-label'))) {
            $el.attr('aria-labelledby', $(openelement).attr('id'));
        }

        // Handler: Show popup when clicked on `open` element
        $(document).on('click', openelement, function (event) {
            event.preventDefault();

            var ord = $(this).data('popup-ordinal');
            setTimeout(function() { // setTimeout is to allow `close` method to finish (for issues with multiple tooltips)
                methods.show(el, ord);
            }, 0);
        });

        // Handler: `close` element
        var closeelement = (options.closeelement) ? options.closeelement : ('.' + el.id + closesuffix);
        $(document).on('click', closeelement, function (e) {
            methods.hide(el);
            e.preventDefault();
        });

        if (options.detach) {
            $el.hide().detach();
        } else {
            $wrapper.hide();
        }
    },

    /**
     * Show method
     *
     * @param {object} el - popup instance DOM node
     * @param {number} ordinal - order number of an `open` element
     */
    show: function (el, ordinal) {
        var $el = $(el);

        if ($el.data('popup-visible')) return;

        // Initialize if not initialized. Required for: $('#popup').popup('show')
        if (!$el.data('popup-initialized')) {
            methods._init(el);
        }
        $el.attr('data-popup-initialized', 'true');

        var $body = $('body');
        var options = $el.data('popupoptions');
        var $wrapper = $('#' + el.id + '_wrapper');
        var $background = $('#' + el.id + '_background');

        // `beforeopen` callback event
        callback(el, ordinal, options.beforeopen);

        // Remember last clicked place
        lastclicked[el.id] = ordinal;

        // Add popup id to popup stack
        setTimeout(function() {
            stack.push(el.id);
        }, 0);

        // Calculating maximum z-index
        if (options.autozindex) {

            var elements = document.getElementsByTagName('*');
            var len = elements.length;
            var maxzindex = 0;

            for(var i=0; i<len; i++){

                var elementzindex = $(elements[i]).css('z-index');

                if(elementzindex !== 'auto'){

                  elementzindex = parseInt(elementzindex, 10);

                  if(maxzindex < elementzindex){
                    maxzindex = elementzindex;
                  }
                }
            }

            zindexvalues[el.id] = maxzindex;

            // Add z-index to the background
            if (options.background) {
                if (zindexvalues[el.id] > 0) {
                    $('#' + el.id + '_background').css({
                        zIndex: (zindexvalues[el.id] + 1)
                    });
                }
            }

            // Add z-index to the wrapper
            if (zindexvalues[el.id] > 0) {
                $wrapper.css({
                    zIndex: (zindexvalues[el.id] + 2)
                });
            }
        }

        if (options.detach) {
            $wrapper.prepend(el);
            $el.show();
        } else {
            $wrapper.show();
        }

        opentimer = setTimeout(function() {
            $wrapper.css({
                visibility: 'visible',
                opacity: 1
            });

            $('html').addClass('popup_visible').addClass('popup_visible_' + el.id);
            $el.addClass('popup_content_visible');
        }, 20); // 20ms required for opening animation to occur in FF

        // Disable background layer scrolling when popup is opened
        if (options.scrolllock) {
            $body.css('overflow', 'hidden');
            if ($body.height() > $window.height()) {
                $body.css('margin-right', bodymarginright + scrollbarwidth);
            }
        }

        if(options.backgroundactive){
            //calculates the vertical align
            $el.css({
                top:(
                    $window.height() - (
                        $el.get(0).offsetHeight +
                        parseInt($el.css('margin-top'), 10) +
                        parseInt($el.css('margin-bottom'), 10)
                    )
                )/2 +'px',
            });
        }

        $el.css({
            'visibility': 'visible',
            'opacity': 1
        });

        // Show background
        if (options.background) {
            $background.css({
                'visibility': 'visible',
                'opacity': options.opacity
            });

            // Fix IE8 issue with background not appearing
            setTimeout(function() {
                $background.css({
                    'opacity': options.opacity
                });
            }, 0);
        }

        $el.data('popup-visible', true);

        // Position popup
        methods.reposition(el, ordinal);

        // Remember which element had focus before opening a popup
        $el.data('focusedelementbeforepopup', document.activeElement);

        // Handler: Keep focus inside dialog box
        if (options.keepfocus) {
            // Make holder div focusable
            $el.attr('tabindex', -1);

            // Focus popup or user specified element.
            // Initial timeout of 50ms is set to give some time to popup to show after clicking on
            // `open` element, and after animation is complete to prevent background scrolling.
            setTimeout(function() {
                if (options.focuselement === 'closebutton') {
                    $('#' + el.id + ' .' + el.id + closesuffix + ':first').focus();
                } else if (options.focuselement) {
                    $(options.focuselement).focus();
                } else {
                    $el.focus();
                }
            }, options.focusdelay);

        }

        // Hide main content from screen readers
        $(options.pagecontainer).attr('aria-hidden', true);

        // Reveal popup content to screen readers
        $el.attr('aria-hidden', false);

        callback(el, ordinal, options.onopen);

        if (transitionsupport) {
            $wrapper.one('transitionend', function() {
                callback(el, ordinal, options.opentransitionend);
            });
        } else {
            callback(el, ordinal, options.opentransitionend);
        }
    },

    /**
     * Hide method
     *
     * @param {object} el - popup instance DOM node
     */
    hide: function (el) {
        if(opentimer) clearTimeout(opentimer);

        var $body = $('body');
        var $el = $(el);
        var options = $el.data('popupoptions');
        var $wrapper = $('#' + el.id + '_wrapper');
        var $background = $('#' + el.id + '_background');

        $el.data('popup-visible', false);


        if (stack.length === 1) {
            $('html').removeClass('popup_visible').removeClass('popup_visible_' + el.id);
        } else {
            $('html').removeClass('popup_visible_' + el.id);
        }

        // Remove last opened popup from the stack
        stack.pop();

        $el.removeClass('popup_content_visible');

        if (options.keepfocus) {
            // Focus back on saved element
            setTimeout(function() {
                if ($($el.data('focusedelementbeforepopup')).is(':visible')) {
                    $el.data('focusedelementbeforepopup').focus();
                }
            }, 0);
        }

        // Hide popup
        $wrapper.css({
            'visibility': 'hidden',
            'opacity': 0
        });
        $el.css({
            'visibility': 'hidden',
            'opacity': 0
        });

        // Hide background
        if (options.background) {
            $background.css({
                'visibility': 'hidden',
                'opacity': 0
            });
        }

        // Reveal main content to screen readers
        $(options.pagecontainer).attr('aria-hidden', false);

        // Hide popup content from screen readers
        $el.attr('aria-hidden', true);

        // `onclose` callback event
        callback(el, lastclicked[el.id], options.onclose);

        if (transitionsupport && $el.css('transition-duration') !== '0s') {
            $el.one('transitionend', function(e) {

                if (!($el.data('popup-visible'))) {
                    if (options.detach) {
                        $el.hide().detach();
                    } else {
                        $wrapper.hide();
                    }
                }

                // Re-enable scrolling of background layer
                if (options.scrolllock) {
                    setTimeout(function() {
                        $body.css({
                            overflow: 'visible',
                            'margin-right': bodymarginright
                        });
                    }, 10); // 10ms added for CSS transition in Firefox which doesn't like overflow:auto
                }

                callback(el, lastclicked[el.id], options.closetransitionend);
            });
        } else {
            if (options.detach) {
                $el.hide().detach();
            } else {
                $wrapper.hide();
            }

            // Re-enable scrolling of background layer
            if (options.scrolllock) {
                setTimeout(function() {
                    $body.css({
                        overflow: 'visible',
                        'margin-right': bodymarginright
                    });
                }, 10); // 10ms added for CSS transition in Firefox which doesn't like overflow:auto
            }

            callback(el, lastclicked[el.id], options.closetransitionend);
        }

    },

    /**
     * Toggle method
     *
     * @param {object} el - popup instance DOM node
     * @param {number} ordinal - order number of an `open` element
     */
    toggle: function (el, ordinal) {
        if ($(el).data('popup-visible')) {
            methods.hide(el);
        } else {
            setTimeout(function() {
                methods.show(el, ordinal);
            }, 0);
        }
    },

    /**
     * Reposition method
     *
     * @param {object} el - popup instance DOM node
     * @param {number} ordinal - order number of an `open` element
     */
    reposition: function (el, ordinal) {
        var $el = $(el);
        var options = $el.data('popupoptions');
        var $wrapper = $('#' + el.id + '_wrapper');
        var $background = $('#' + el.id + '_background');

        ordinal = ordinal || 0;

        // Tooltip type
        if (options.type == 'tooltip') {
            $wrapper.css({
                'position': 'absolute'
            });

            var $tooltipanchor;
            if (options.tooltipanchor) {
                $tooltipanchor = $(options.tooltipanchor);
            } else if (options.openelement) {
                $tooltipanchor = $(options.openelement).filter('[data-popup-ordinal="' + ordinal + '"]');
            } else {
                $tooltipanchor = $('.' + el.id + opensuffix + '[data-popup-ordinal="' + ordinal + '"]');
            }

            var linkOffset = $tooltipanchor.offset();

            // Horizontal position for tooltip
            if (options.horizontal == 'right') {
                $wrapper.css('left', linkOffset.left + $tooltipanchor.outerWidth() + options.offsetleft);
            } else if (options.horizontal == 'leftedge') {
                $wrapper.css('left', linkOffset.left + $tooltipanchor.outerWidth() - $tooltipanchor.outerWidth() +  options.offsetleft);
            } else if (options.horizontal == 'left') {
                $wrapper.css('right', $window.width() - linkOffset.left  - options.offsetleft);
            } else if (options.horizontal == 'rightedge') {
                $wrapper.css('right', $window.width()  - linkOffset.left - $tooltipanchor.outerWidth() - options.offsetleft);
            } else {
                $wrapper.css('left', linkOffset.left + ($tooltipanchor.outerWidth() / 2) - ($el.outerWidth() / 2) - parseFloat($el.css('marginLeft')) + options.offsetleft);
            }

            // Vertical position for tooltip
            if (options.vertical == 'bottom') {
                $wrapper.css('top', linkOffset.top + $tooltipanchor.outerHeight() + options.offsettop);
            } else if (options.vertical == 'bottomedge') {
                $wrapper.css('top', linkOffset.top + $tooltipanchor.outerHeight() - $el.outerHeight() + options.offsettop);
            } else if (options.vertical == 'top') {
                $wrapper.css('bottom', $window.height() - linkOffset.top - options.offsettop);
            } else if (options.vertical == 'topedge') {
                $wrapper.css('bottom', $window.height() - linkOffset.top - $el.outerHeight() - options.offsettop);
            } else {
                $wrapper.css('top', linkOffset.top + ($tooltipanchor.outerHeight() / 2) - ($el.outerHeight() / 2) - parseFloat($el.css('marginTop')) + options.offsettop);
            }

        // Overlay type
        } else if (options.type == 'overlay') {

            // Horizontal position for overlay
            if (options.horizontal) {
                $wrapper.css('text-align', options.horizontal);
            } else {
                $wrapper.css('text-align', 'center');
            }

            // Vertical position for overlay
            if (options.vertical) {
                $el.css('vertical-align', options.vertical);
            } else {
                $el.css('vertical-align', 'middle');
            }
        }
    }

};

/**
 * Callback event calls
 *
 * @param {object} el - popup instance DOM node
 * @param {number} ordinal - order number of an `open` element
 * @param {function} func - callback function
 */
var callback = function (el, ordinal, func) {
    var openelement =  (options.openelement) ? options.openelement : ('.' + el.id + opensuffix);
    var elementclicked = $(openelement + '[data-popup-ordinal="' + ordinal + '"]');
    if (typeof func == 'function') {
        func(elementclicked);
    }
};

// Hide popup if ESC key is pressed
$(document).on('keydown', function (event) {
    if(stack.length) {
        var elementId = stack[stack.length - 1];
        var el = document.getElementById(elementId);

        if ($(el).data('popupoptions').escape && event.keyCode == 27 && $(el).data('popup-visible')) {
            methods.hide(el);
        }
    }
});

// Hide popup if clicked outside of it
$(document).on('click touchstart', function (event) {
    if(stack.length) {
        var elementId = stack[stack.length - 1];
        var el = document.getElementById(elementId);

        if ($(el).data('popupoptions').blur && !$(event.target).parents().andSelf().is('#' + elementId) && $(el).data('popup-visible') && event.which !== 2) {
            methods.hide(el);

            if ($(el).data('popupoptions').type === 'overlay') {
                event.preventDefault(); // iOS will trigger click on the links below the overlay when clicked on the overlay if we don't prevent default action
            }
        }
    }
});

// Keep keyboard focus inside of popup
$(document).on('focusin', function(event) {
    if(stack.length) {
        var elementId = stack[stack.length - 1];
        var el = document.getElementById(elementId);

        if ($(el).data('popupoptions').keepfocus) {
            if (!el.contains(event.target)) {
                event.stopPropagation();
                el.focus();
            }
        }
    }
});

/**
 * Plugin API
 */
$.fn.popup = function (customoptions) {
    return this.each(function () {

        $el = $(this);

        if (typeof customoptions === 'object') {  // e.g. $('#popup').popup({'color':'blue'})
            var opt = $.extend({}, $.fn.popup.defaults, customoptions);
            $el.data('popupoptions', opt);
            options = $el.data('popupoptions');

            methods._init(this);

        } else if (typeof customoptions === 'string') { // e.g. $('#popup').popup('hide')
            if (!($el.data('popupoptions'))) {
                $el.data('popupoptions', $.fn.popup.defaults);
                options = $el.data('popupoptions');
            }

            methods[customoptions].call(this, this);

        } else { // e.g. $('#popup').popup()
            if (!($el.data('popupoptions'))) {
                $el.data('popupoptions', $.fn.popup.defaults);
                options = $el.data('popupoptions');
            }

            methods._init(this);

        }

    });
};

$.fn.popup.defaults = {
    type: 'overlay',
    autoopen: false,
    background: true,
    backgroundactive: false,
    color: 'black',
    opacity: '0.5',
    horizontal: 'center',
    vertical: 'middle',
    offsettop: 0,
    offsetleft: 0,
    escape: true,
    blur: true,
    setzindex: true,
    autozindex: false,
    scrolllock: false,
    keepfocus: true,
    focuselement: null,
    focusdelay: 50,
    outline: false,
    pagecontainer: null,
    detach: false,
    openelement: null,
    closeelement: null,
    transition: null,
    tooltipanchor: null,
    beforeopen: null,
    onclose: null,
    onopen: null,
    opentransitionend: null,
    closetransitionend: null
};

0 个答案:

没有答案