position:绝对元素未正确定位

时间:2012-11-01 17:29:44

标签: javascript jquery css

我正在创建我的第一个jQuery插件,并在下面有一个有点工作的例子(参见http://tapmeister.com/test/selector.html)和脚本。我一直在努力将元素定位好几天。

所需的外观和功能从显示文本右侧的一些文本和可选向下箭头开始。单击后,文本/箭头将替换为选项列表,其中突出显示当前所选选项并位于原始文本的相同位置。单击选项使其成为当前选定的选项,并启动用户定义的回调,并单击任何其他空格将关闭对话框。

插件替换原始的select元素,我希望保留它是内联还是块。但是,在使它们内联时,位置:绝对<ul class="selectIt-list">弹出窗口不会出现在其关联文本<span class="selectIt-text">上方。由于span文本和ul popup都在位置:relative <div class="selectIt">,为什么它位于最左边?

作为旁注,我这样做的一个主要原因是要了解如何构建插件的一致模式。我强烈建立http://docs.jquery.com/Plugins/Authoring的方法(任何理由我都不应该这样做)。我希望有人可以查看它,确认我是否正确解释如何构建插件,并提供任何建议。我也不确定我是否真的得到了整个名称空间。也许我应该把它作为一个单独的问题发布?但我不知道是否要求代码批评是合适的。

谢谢!

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
    <head> 
        <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /> 
        <title>Testing</title>  
        <script src="http://code.jquery.com/jquery-latest.js"></script> 
        <style type="text/css">

            /* CSS associated with plugin */

            div.selectIt {
                position:relative;
            }

            span.selectIt-text {
                color: #2C46B7;
                cursor: pointer;
                font-family: Verdana;
                font-size: 10px;
                display:inline;
            }

            span.selectIt-text,
            ul.selectIt-list {
                padding: 7px 3px 5px;
            }

            ul.selectIt-list {
                display:none;
                margin:0px;
                position: absolute;
                background-color: #FFFFFF;
                border: 1px solid #B3B4A7;
                z-index: 50;
                box-shadow: 3px 1px 6px #505050;
            }

            ul.selectIt-list li {
                background-color: #FFFFFF;
                color: #393926;
                list-style-type: none;
                font-family: Verdana;
                font-size: 10px;
                margin-bottom: 2px;
                padding-left: 2px;
                padding-right: 2px;
            }
            ul.selectIt-list li.selectIt-hover {
                background-color: #DFDED8;
                color: #393926;
            }
            ul.selectIt-list li.selectIt-selected {
                background-color: #737060;
                color: #FFFFFF;
                cursor: default;
            }

            /* CSS Not associated with plugin */

            ul.myList > li {
                list-style-type: none;
                display:inline;
            }
            #horizontalList select.mySelect {
                display:inline;
            }
            #verticalList select.mySelect {
                display:block;
            }
            span.label {
                color: rgb(57, 57, 38);
                font-family: Verdana;
                font-size: 10px;
                font-weight: bold;
            }
            span.selectIt-text {
                background-image: url("dropdown_arrow_blue.gif");
                background-position:right center; 
                background-repeat: no-repeat;
                padding-right: 10px; /*Override padding-right to allow for image*/
            }

        </style> 

        <script> 
            /*
            * jQuery SelectIT
            * Copyright 2012 Michael Reed
            * Dual licensed under the MIT and GPL licenses.
            * 
            */
            (function( $ ){

                var methods = {
                    init : function( options ) {
                        //console.log('init');

                        // Create some defaults, extending them with any options that were provided
                        var settings = $.extend({
                            'class' : null, //Applies custom class so user may override
                            'extraWidth' : 0,  //Extra space to add to width to allow for user image css
                            'click' : function(){}   //Some user defined code.
                            }, options  || {}); //Just in case user doesn't provide options

                        //Apply events that should only happen once (unlike what is done by http://docs.jquery.com/Plugins/Authoring#Events. Why did they do it that way?)
                        $(document).on('click.selectIt',methods.close);

                        return this.each(function(){

                            var $t = $(this),
                            data = $t.data('selectIt');

                            //Replace element
                            var list = $('<ul/>', {'class': "selectIt-list" }),
                            length,
                            max=0;
                            $t.find('option').each(function(i){
                                var $this=$(this),
                                text=$this.text(),
                                length = parseInt($this.css('width'));
                                max = length > max ? length : max;
                                list.append( $('<li/>', {text: text}))
                            });
                            max=max+settings.extraWidth;
                            list.css('width',max+'px');

                            var selectIt = $('<div/>', {class:"selectIt"+((settings.class)?' '+settings.class:'')}) //Didn't work to display same as original element: display:$t.css('display')
                            .append($('<span/>', {class:'selectIt-text',text:$t.find(":selected").text()}))
                            .append(list)
                            .css({display:$t.css('display')});  //Set to same as original element

                            //Apply events
                            selectIt.on('click.selectIt','span.selectIt-text',methods.open);
                            selectIt.on('mouseenter.selectIt','ul.selectIt-list li',methods.mouseenter);
                            selectIt.on('mouseleave.selectIt','ul.selectIt-list li',methods.mouseleave);
                            selectIt.on('click.selectIt','ul.selectIt-list li',methods.click);

                            // If the plugin hasn't been initialized yet.  Why?
                            if ( ! data ) {
                                // Do more setup stuff here
                                $t.data('selectIt', {
                                    target : $t,     //Not really sure where this will be used
                                    selectIt : selectIt,  //Will be used when removing in destroy method
                                    settings: settings  //Save here so other methods have access
                                });

                            }
                            $t.hide().after(selectIt);
                        });
                    },

                    destroy : function(e) {
                        //console.log('destroy');
                        return this.each(function(){
                            var $t = $(this);
                            $t.off('.selectIt'); //Removes any events in selectIt namespace
                            $t.data('selectIt').selectIt.remove();   //Removes element from page
                            $t.removeData('selectIt');   //Removes data in selectIt namespace
                            //Should the original element be made visible?
                        })
                    },

                    open : function(e) {
                        //console.log('open');
                        methods.close();
                        var $t=$(this),
                        $p=$t.parent(),
                        index=$p.prev().prop("selectedIndex"),
                        list=$p.find('ul.selectIt-list').show();
                        list.find('li.selectIt-selected').removeClass('selectIt-selected');
                        list.find('li').eq(index).addClass('selectIt-selected');
                        var top = 0 - list.find('li').eq(index).position().top;
                        list.css({top: top});
                        e.stopPropagation();    //Don't trigger documents click
                    },

                    //If this is all I am doing, can change to just using CSS psudo class.
                    mouseenter : function(e) {
                        $(this).addClass('selectIt-hover');
                    },
                    mouseleave : function(e) {
                        $(this).removeClass('selectIt-hover');
                    },

                    click : function(e) {
                        //console.log('click');
                        var $t=$(this);
                        if(!$t.hasClass('selectIt-selected'))
                        {
                            var $p=$t.parent(),
                            $pp=$p.parent(),
                            select=$pp.prev(),
                            index=$t.index(),
                            option=select.find('option').eq(index),
                            value=(value=option.val())?value:option.text();
                            select.val(value).prop('selectedIndex',index);
                            $pp.find('span.selectIt-text').text($t.text());
                            select.data('selectIt').settings.click.call(this,value);
                        }
                    },

                    close : function(e) {
                        //console.log('close');
                        $('div.selectIt').find('ul.selectIt-list').hide().parent();//.find('span.selectIt-text').show();
                    },

                    update : function(content) {alert('When will this type of method be used?');}
                };

                $.fn.selectIt = function(method) {
                    if ( methods[method] ) {
                        return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
                    } else if ( typeof method === 'object' || ! method ) {
                        return methods.init.apply( this, arguments );
                    } else {
                        $.error( 'Method ' +  method + ' does not exist on jQuery.selectIt' );
                    }    
                };

            })( jQuery );

            $(function(){
                $('select.mySelect').selectIt({
                    //'class' : 'whatever',
                    'extraWidth' : 0,
                    'click':function(value){
                        switch(value)
                        {
                            case 'first':console.log('Do something first');break;
                            case 'second':console.log('Do something second');break;
                            case 'third':console.log('Do something third');break;
                            default: console.log('Do something default');
                        }
                    }
                });

                $('#horizontalList button.destroy').click(function(){
                    $('#horizontalList select.mySelect').selectIt('destroy');
                });

               $('#verticalList button.destroy').click(function(){
                    $('#verticalList select.mySelect').selectIt('destroy');
                });
            });
        </script>
    </head>

    <body>

        <div id="horizontalList">
            <h1>Horizontal List</h1>

            <ul class="myList">
                <li>
                    <span class="label">Label</span>
                    <select class="mySelect">
                        <option value="first">First</option>
                        <option value="second" selected="selected">Second</option>
                        <option>Third</option>
                    </select>
                </li>
                <li>
                    <span class="label">Some Label</span>
                    <select class="mySelect">
                        <option value="first">First</option>
                        <option value="second" selected="selected">Second</option>
                        <option value="third">Third</option>
                    </select>
                </li>
                <li>
                    <span class="label">Some Label</span>
                    <select class="mySelect">
                        <option value="">First</option>
                        <option value="second">Second</option>
                        <option value="third">Third</option>
                    </select>
                </li>
            </ul>
            <button class="destroy">Destroy</button>
        </div>
        <div id="verticalList">
            <h1>Vertical List</h1>

            <ul class="myList">
                <li>
                    <span class="label">Label</span>
                    <select class="mySelect">
                        <option value="first">First</option>
                        <option value="second" selected="selected">Second</option>
                        <option>Third</option>
                    </select>
                </li>
                <li>
                    <span class="label">Some Label</span>
                    <select class="mySelect">
                        <option value="first">First</option>
                        <option value="second" selected="selected">Second</option>
                        <option value="third">Third</option>
                    </select>
                </li>
                <li>
                    <span class="label">Some Label</span>
                    <select class="mySelect">
                        <option value="">First</option>
                        <option value="second">Second</option>
                        <option value="third">Third</option>
                    </select>
                </li>
            </ul>
            <button class="destroy">Destroy</button>
        </div>
    </body> 
</html> 

2 个答案:

答案 0 :(得分:1)

为了定位您所描述的内容,我强烈建议您使用 jQuery的“my at of”定位。这是一个巨大的头痛救星!

http://docs.jquery.com/UI/API/1.8/Position

应用于您的示例,它看起来像:

//PLACE INSIDE YOUR EVENT HANDLER
$(".selectIt-list").position({
    my: "bottom center",
    at: "top center",
    of: ".selectIt-text",
  });

答案 1 :(得分:1)

JQuery中有一行[第173行]:

list.css({top: top});

在它下面插入这一行:

list.css({left: $(this).position().left});