Combo drop down off screen (v461)

It seems that the combo dropdown (select) is always positioned below and left justified to the combo itself. This causes part of the dropdown to be off screen if the combo is itself near the right edge or bottom of the visible form.

It also seems that there were autoPositioning capabilities prior to version 4 that were removed.

How can I use a combo anywhere on the screen and have the dropdown be visible to the user?

Hi,

If you are using fullscreen init, combo must change the popup list position when there is no enough space at the bottom of the screen.
Please check
snippet.dhtmlx.com/d71f2ec4f

I’m using absolute positioning of the combo in the form, along with multicolumn select contents so that the dropdown is wider than the combo itself. When the combo is near the right edge of the form, then the dropdown (which is left justified with the combo) is largely off screen. I’ve attached an image file showing this problem.

There is no built-in functionality to fix such kind of screen overflow.
We will consider adding some workaround in next builds.

I’ve been looking into this mod and discovered an error in your existing code…at least in v4.6.1

line 1348 in dhtmlxcombo.js is:

var onTop = Math.max(0, Math.floor((by+hh-s.top)/this.conf.item_h));
but should be:

var onTop = Math.max(0, Math.floor((by-hh-s.top)/this.conf.item_h));
That is, the header height (hh) must be subtracted from the available height, not added to it. I’ve tested and confirmed this fix.

Yep, you are right.
Thanks for the patch, it will be included in the next build.

Below are my overrides to handle positioning of the select list and account for screen boundaries:

/*
make combo select positioning account for edges of visible browser page
start combo select positioning change
from v4.6.1 dhtmlx_debug.js
*/
dhtmlXCombo.prototype._showList = function(autoHide) {

    if (this._getListVisibleCount() == 0) {
        if (autoHide && this._isListVisible()) this._hideList();
        return;
    }

    if (this._isListVisible()) {
        this._checkListHeight();
        return;
    }

    this.list.style.zIndex = window.dhx4.zim.reserve(this.conf.list_zi_id); // get new z-index
    if (this.hdr != null && this.conf.template.header == true) this.hdr.style.zIndex = Number(this.list.style.zIndex)+1;

    this.list.style.visibility = "hidden";
    this.list.style.display = "";
    if (this.hdr != null && this.conf.template.header == true) {
        this.hdr.style.visibility = this.list.style.visibility;
        this.hdr.style.display = this.list.style.display;
    }

    // position
    var h0 = (this.hdr != null && this.conf.template.header == true ? this.hdr.offsetHeight : 0);

    /*
     REW: adjust left to prevent pushing contents of list off screen to the right,
     but only to the left screen boundary and
     limit list width to available screen width so that scrollbar is always accessible
     */

    var lWidth = Math.max(this.conf.opts_width||this.conf.col_w||0, this.conf.combo_width);

    var s = window.dhx4.screenDim();
    lWidth = Math.min(s.right,lWidth);

    this.list.style.width = lWidth+"px";
    //this.list.style.width = Math.max(this.conf.opts_width||this.conf.col_w||0, this.conf.combo_width)+"px";

    this.list.style.top = window.dhx4.absTop(this.base)+h0+this.base.offsetHeight-1+"px";

    var absLeft = window.dhx4.absLeft(this.base);
    var leftAdjust = (absLeft+lWidth < s.right ? 0:Math.min(lWidth - (s.right - absLeft - 4), absLeft));

    this.list.style.left = (window.dhx4.absLeft(this.base)-leftAdjust)+"px";

    if (this.hdr != null && this.conf.template.header == true) {
        this.hdr.style.width = this.list.style.width;
        this.hdr.style.left = this.list.style.left;
        this.hdr.style.top = parseInt(this.list.style.top)-h0+"px";
    }

    // height
    this._checkListHeight();

    // check bottom overlay
    this.list.style.visibility = "visible";
    if (this.hdr != null && this.conf.template.header == true) this.hdr.style.visibility = "visible";

    this.callEvent("onOpen",[]);

};

dhtmlXCombo.prototype._checkListHeight = function() {

    /*
     REW: appears to try to put list on bottom by reducing the num of items shown down to opts_count_min,
     and only moving above the base if the min fails to fit. The onTop calc above was wrong.
     original values:  opts_count_min: 3; opts_count: 8;
     Don't want to show only 3 items just to display below the combo, rather show a usable quantity above

     Changes:
     opts_count_min: 8;
     opts_count: 15;

     */

    this.conf.opts_count_min = 8; //override
    this.conf.opts_count = 15; //override

    if (!this._isListVisible()) return;

    if (this.conf.item_h == null) {
        var item = this.list.firstChild;
        while (item != null) {
            if (item.style.display == "") {
                this.conf.item_h = item.offsetHeight + (this.hdr != null ? -1 : 0); // multicol rows have -1px margin
                item = null;
            } else {
                item = item.nextSibling;
            }
        }
        item = null;
    }


    var s = window.dhx4.screenDim();
    var by = window.dhx4.absTop(this.base);
    var bh = this.base.offsetHeight;
    var hh = (this.hdr!=null&&this.conf.template.header==true?this.hdr.offsetHeight:0); // header_height

    //REW corrected 12.10.2016
    var onTop = Math.max(0, Math.floor((by-hh-s.top)/this.conf.item_h));

    var onBottom = Math.max(0, Math.floor((s.bottom-(by+bh+hh))/this.conf.item_h));

    var itemsCount = this._getListVisibleCount();

    // top/bottom detect
    if (onBottom < Math.min(this.conf.opts_count_min, itemsCount) && onTop > onBottom) onBottom = null;

    var itemsToShow = Math.min((onBottom==null?onTop:onBottom), this.conf.opts_count, itemsCount);
    var h = (itemsToShow<itemsCount?(itemsToShow*this.conf.item_h)+"px":"");

    var ofs = this.conf.sp[this.conf.skin][this.hdr!=null&&this.conf.template.header==true?"hdr_ofs":"list_ofs"];

    this.list.style.height = h;
    this.list.style.top = (onBottom==null?by-this.list.offsetHeight+ofs:by+bh+hh-ofs)+"px";
    if (this.hdr != null && this.conf.template.header == true) this.hdr.style.top = (onBottom==null?by-hh-this.list.offsetHeight+ofs:by+bh-ofs)+"px";

};