如何将自动完成菜单放置在文本输入框上方?

9
有没有办法将自动完成结果集显示在文本输入框上方?
问题在于我在lightbox应用程序中使用它。背景元素被设置为页面高度和宽度的100%,它自然不会随着内容而扩展。因此,如果文本框上方的内容增加得太多,以至于输入框被强制移到页面底部,然后用户在其中键入某些内容,则自动完成菜单将出现,并且由于输入位于页面末尾,菜单会导致滚动条出现。
由于背景元素(深色)不会扩展以适应它,因为菜单是绝对定位的,所以看起来很丑陋,页面底部呈白色(与实际页面颜色一致),而页面顶部则为深色。
那么有没有办法在自动完成菜单出现之前检查输入位置?如果它距离顶部超过300px,则菜单应该出现在输入框上方吗?这是代码:
<!DOCTYPE html>
<html>
<head>
<title>Filter</title>
<link type="text/css" href="ui/ui.css" rel="stylesheet" />
        <script type="text/javascript" src="jquery.js"></script>
        <script type="text/javascript" src="ui.js"></script>
<style>
div.filtertab { display: inline-block; height: 30px; background:url(filter.png); border-radius: 5px; position: relative;  margin: 10px 45px 10px 8px; cursor: pointer; -moz-user-select: none; box-shadow: 4px 2px 2px #aaaaaa; }

div.filtertab span.filtertext { position: relative;  margin-top: 4px; margin-left: 11px; margin-right: 1px;font-family:cursive,"Comic Sans"; font-size: 0.9em; float: left; display: inline-block; }

span.arrow { display: inline-block; border-top: 5px solid transparent; width: 0px; height: 0px; border-bottom: 5px solid transparent; border-right: 8.66px solid transparent; border-left: 8.66px solid #333333; margin-left: 7px; margin-top: 10.5px; }

.dateheaders { background: url(select.png); width: 120px; height: 28px; position: absolute; top: 0px; left: 0px; border-radius: 5px; cursor: pointer; display: inline-block; z-index: 55; }

.toheader { left: 183px }

.spandatew { font-weight: bold; position: relative; top: 2px; left: 6px; color: white; font-family: "lucida grande",tahoma,verdana,arial,sans-serif; font-weight: bold; color: white; font-size: 0.8em; }

div.arrow { height: 0px; width: 0px; position: absolute; display: inline-block; border-bottom: 5px solid transparent; border-right: 5px solid transparent; border-left: 5px solid transparent; border-top: 5px solid white;  right: 8px; top: 12px; }

input#from,input#to {width:107px;height:20px; position: relative; top: -30px; opacity: 0.01; cursor: pointer; }

input#from { left: 22px; }

input#to { left: 79px; }

div#ui-datepicker-div { left: -10px; }

div#diva1 {  width: 190px; position: absolute; display: none; z-index: 10; outline: none;}

div#diva2 {  width: 190px; position: absolute; display: none; z-index: 10; outline: none;}

#datecontainer { position: relative; width: 400px; height: 30px; background: red; left: 10px; }

.inpdiv { position: relative; margin-top: 6px; margin-left: 7px; background: #E2E6FF;  border-radius: 3px;  margin-bottom: 5px; display inline-block; float: left; margin-right: 3px; }

.inpdiv span.content { position: relative; top: 1px; left: 2px; padding: 2px 5px 6px 2px; font-size: 0.9em; display: inline-block; font-family: cursive,"Comic Sans";}

.inpdiv span.cancel { display: inline-block; border-radius: 3px; position: relative; top: 5px; margin: 3px 3px 3px 4px; height: 12px; width:12px; background: #bdbbae; cursor: pointer; }

.letter { position: absolute; display: inline-block; right: 4.4px; top: 4.7px; cursor: pointer; FONT-FAMILY: SANS-SERIF; FONT-SIZE: 0.9EM; }

div.friends { width: 400px;   position: relative;  overflow: hidden; border: 1px solid #4496e7; border-radius: 5px; cursor: text; height: 35px; height: auto !important; min-height: 35px;  left: 5px; }
div.friends2 { margin-top: 10px; }

div.friendsc { position: relative; top: 100px; left: 350px; width: 410px;  padding: 10px 0px; height: auto !important; height: 80px; min-height: 80px; background: green;}

div.clear { clear: both; width: 100%; height: 10px; }

button { position: absolute; top: 300px; left: 450px; }

input { position: relative; margin: 9px 0px 10px 10px; border: 0px solid white; float: left; max-width: 382px; }

div#outerfilter { width: 900px; background: grey;  position: relative; min-height: 400px; max-height: 500px; margin:auto; top: 10px; overflow: auto; padding-bottom: 20px; }
div#innerfilter { position: relative; top: 0px; left: 0px; width: 880px; min-height: 400px;background: #eaeaea; }
.filtersection { position: relative; float: left; width: 440px; min-height: 400px; height: auto !important; }
</style>
<script type="text/javascript">
$(document).ready(function(){
(function($){

$.fn.autoGrowInput = function(o) {

            o = $.extend({
                maxWidth: 1000,
                minWidth: 0,
                comfortZone: 70
            }, o);

            this.filter('input:text').each(function(){

                var minWidth = o.minWidth || $(this).width(),
                    val = '',
                    input = $(this),
                    testSubject = $('<tester/>').css({
                        position: 'absolute',
                        top: -9999,
                        left: -9999,
                        width: 'auto',
                        fontSize: input.css('fontSize'),
                        fontFamily: input.css('fontFamily'),
                        fontWeight: input.css('fontWeight'),
                        letterSpacing: input.css('letterSpacing'),
                        whiteSpace: 'nowrap'
                    }),
                    check = function() {

                        if (val === (val = input.val())) {return;}

                        // Enter new content into testSubject
                        var escaped = val.replace(/&/g, '&amp;').replace(/\s/g,'&nbsp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
                        testSubject.html(escaped);

                        // Calculate new width + whether to change
                        var testerWidth = testSubject.width(),
                            newWidth = Math.min((testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth, o.maxWidth - 1),
                            currentWidth = input.width(),
                            isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth)
                                                 || (newWidth > minWidth && newWidth < o.maxWidth);

                        // Animate width
                        if (isValidWidthChange) {
                            input.width(newWidth);
                        }

                    };

                testSubject.insertAfter(input);

                $(this).bind('keyup keydown blur update', check);

            });

            return this;

        };

    })(jQuery);

arr = ["Soumyashree Chakraborty", "Payel","Jinia","Soujinia","Apoorva","Mona","Shamarpita","Pratik","Pranendra","Bhushan","Bijesh","Salma","Swift","Anushka","Radhe","Amol","Bardha","Sujata","Rohit","Amit","Anuradha","Amrita","Ajay","Sumil","Sachin","Sourav","Anmol","Britannia","Anamika","Priyanka"];
$("span.letter").live('click',function() {
$(this).parent().parent().find("input").focus();
$(this).parent().remove();
  });

options = {
            source: arr.slice(0,5),
            minLength: 1,
            select: function(event,ui) {
            $('<span class = "inpdiv"><span class = "content">'+ui.item.value+'</span><span class = "cancel"></span><span class="letter">X</span></span>').replaceAll(this);
            $("<input type='text' size='3' />").appendTo(curautocomp).focus();
            event.preventDefault();
            },
            focus: function(e,ui) {
            e.preventDefault();
            },
            autoFocus: true,
            delay: 0
            };
$("input").live("focus", function (event) {
curautocomp = $(this).parent();
$(this).autocomplete(options);
$(this).autoGrowInput({
    comfortZone: 5,
    minWidth: 15,
    maxWidth: 382
});
});
















});
</script>
</head>
<body>
<div id = 'outerfilter'>
<div id = 'innerfilter'>
<div class = 'filtersection'>
<div class="filtertab"><span class="filtertext">Filter by Forum</span><span class='arrow'></span></div>
<br />
<div class = "friends">
<input type = "text" size = "2" value = "" />
</div>
<div class="filtertab"><span class="filtertext">Not Asked by</span><span class='arrow'></span></div>
<br />
<div class = "friends">
<input type = "text" size = "2" value = "" />
</div>
<div class="filtertab"><span class="filtertext">Has No Answer by</span><span class='arrow'></span></div>
<br />
<div class = "friends">
<input type = "text" size = "2" value = "" />
</div>
<div class="filtertab"><span class="filtertext">No of Answers</span><span class='arrow'></span></div>
<br />
<div class = "friends">
<input type = "text" size = "2" value = "" />
</div>
</div>
<div class = 'filtersection'>
<div class="filtertab"><span class="filtertext">Asked by</span><span class='arrow'></span></div>
<br />
<div class = "friends">
<input type = "text" size = "2" value = "" />
</div>
<div class="filtertab"><span class="filtertext">Has Answer by</span><span class='arrow'></span></div>
<br />
<div class = "friends">
<input type = "text" size = "2" value = "" />
</div>
<div class="filtertab"><span class="filtertext">Filter by Date</span><span class='arrow'></span></div>
<br />
<div id = 'datecontainer'>
<div class = 'dateheaders fromheader'>
<span class = 'spandatew'>Select a date</span>
<div class = 'arrow'></div>
</div>
<div class = 'dateheaders toheader'>
<span class = 'spandatew'>Select a date</span>
<div class = 'arrow'></div>
</div>
<div id = 'diva1'></div>
<div id = 'diva2'></div>
</div>
</div>
<div class = 'clear'></div>
</div>
</div>
</body>
</html>

你应该包含一些演示或源代码,这样我们才能查看 :) - OptimusCrime
源代码非常庞大,有许多CSS文件,从中提取了不同的样式,包含所有这些非常困难! - SexyBeast
网站现在在线吗?我理解你想做什么,但是如果没有看到整个架构,很难找到解决方案。这就像在完全黑暗中没有腿却要行走一样困难。 - OptimusCrime
嗯,好的,告诉你吧,你只需将缩小的核心UI文件(包含一切,大约202 KB大小)作为 ui.js,ui黑暗主题文件作为 ui.css 放在 ui文件夹 中,连同核心JQuery文件一起使用,然后使用我发布的代码。我已经将其简化到必要的程度... - SexyBeast
4个回答

7
你需要在自动完成选项中添加一个open函数。从那里,你可以基于其当前位置和高度调整结果。以下代码应该可以帮助你实现:
open: function(event, ui){
    var $input = $(event.target),
        $results = $input.autocomplete("widget"),
        top = $results.position().top,
        height = $results.height(),
        inputHeight = $input.height(),
        newTop = top - height - inputHeight;

    $results.css("top", newTop + "px");
}

1
谢谢!这正是我想要的!当然,我不希望菜单总是显示在上方,所以我添加了一个检查,如果 $resultsoffset().top + height() 超过文档高度,则只有在最后一行代码中才应该进入,否则应按自然方式在下方显示菜单。再次感谢! - SexyBeast
@Cupidvogel 很高兴它对你有用!几周前我也做了类似的事情,所以还记忆犹新。 - lbstr

5
这将整合Ibstr的解决方案和Cupidvogel的评论,并添加窗口滚动位置检测:
open: function (event, ui) {
    var $input = $(event.target);
    var $results = $input.autocomplete("widget");
    var scrollTop = $(window).scrollTop();
    var top = $results.position().top; 
    var height = $results.outerHeight();
    if (top + height > $(window).innerHeight() + scrollTop) {
        newTop = top - height - $input.outerHeight();
        if (newTop > scrollTop)
            $results.css("top", newTop + "px");
    }
}

请注意,我使用outerHeight而不是height,因为我想考虑边框的厚度。
只有在上方有足够的空间完全适合文本框时,自动完成才会将自己定位在顶部。

1

使用position选项来激活碰撞检测:

    $( "#someElement" ).autocomplete({
       source: [...]
       position: {  collision: "flip"  }
    });​

1
$( "#input_box input" ).autocomplete({
      position: { my: "left bottom", at: "left top"},
      source: availableTags
    });

这段代码的意思是自动完成菜单(my)的“左下角”应该放置在输入框(at)的“左上角”。
试用演示来理解这个概念: https://www.plus2net.com/jquery/msg-demo/autocomplete-position.php

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接