Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get a div-based dropdown to gain a scrollbar instead of extend past the end of the screen using JavaScript and CSS? Struts are also involved

I have a dynamically populated div-based dropdown that extends past the end of the screen when the customer puts lots of entries in it, hiding the lower entries. It is on one column in a set of data entry rows (td elements), so the available space varies on which row is being entered.

Unfortunately this is an application I've inherited, don't have any help with, and don't really have the skill set to maintain, so I would appreciate a bit of 'explain like I'm five'. Also, my apologies for the wall of text.

From extensive Googling and examination of other code in the application I believe an optionsCollection would solve this specific problem, but I need to fire off a function to set other data when the selected value changes and haven't found a way to do that with optionsCollection.

Unfortunately this thing is simply too big to post, and I would likely mangle it if I tried to package the problem in a standalone, so here's what I think is the relevant code.

The div

<div class="empform">
<html:form action="/processBlank">
<div id="divJobClass" style="visibility: hidden; position: absolute; height="50px"
 border-color: #fff; border-style:solid; border-width: 1px; background: white; opacity: 1">
    <table id="tableJobClass" cellspacing="0" style="border-color: #9090ff; border-style:solid; border-width:1px;" cellpadding="0"> <%

ctr = 0;
for (JobClassVO jc : jcList) { // href="setJC(<%=jc.getGuid()% >, '< %=jc.getJcIdDesc()% >')"  %>
    <tr><td><input style="border: none; background: white"  type="text" 
        id="jc<%=ctr%>" size="50" value="<%=jc.getJcIdDesc()%>" readonly="readonly"
        onclick="setJC(<%=jc.getGuid()%>, '<%=jc.getJcIdDesc()%>', <%=ctr%>)"
        onkeydown='jcListCheck(event);'></td></tr><%
ctr++;
}
%>

Where the dropdown gets triggered by clicking on the field

<td>
        <html:hidden name="erfEmployee" property="jcGUIDString" indexed="true"/>
        <html:text name="erfEmployee" property="jcId" indexed="true"
            size="8" maxlength="25" onblur='<%= "isLastRow('JobClass', " + count + ");" %>'
            onclick='<%= "showJcList(" + count + ");" %>' 
            onkeydown='<%= "jcCheck(event," + count + ");" %>'
            onchange='<%= "verifyHoursClass(" + count + ");" %>' readonly="true" />
</td>

isLastRow, jcCheck and onChange are not related to the appearance. The functions used to make the div appear

function showJcList(index) {
    var fld = elem("erfEmployee[" + index + "].erfEeSsnFormatted");
    if (fld.value == "")
        return;
    var div = elem_("divJobClass");
    jcGuidTarget = elem("erfEmployee["+index+"].jcGUIDString");
    jcIdTarget = elem("erfEmployee["+index+"].jcId");
    showList(jcIdTarget, div, jcGuidTarget);
    focusSelected("jc", null, <%=jcList.size()-1%>);
}

function showList(idTarget, div, guidTarget) {
    ddDiv = div;
    if (ddDiv.style.visibility == "visible") {
        ddDiv.style.visibility = "hidden";
        return;
    }
    var iTop = 0, oNode = idTarget, iLeft = 0;
    while(oNode.tagName != "BODY" && oNode.tagName != "HTML") {
        iTop += oNode.offsetTop;
        oNode = oNode.offsetParent;
    }
    oNode = idTarget;
    while(oNode.tagName != "BODY" && oNode.tagName != "HTML") {
        iLeft += oNode.offsetLeft;
        oNode = oNode.offsetParent;
    }
    ddDiv.style.left = "" + iLeft + "px";
    ddDiv.style.top = "" + (iTop + idTarget.offsetHeight) + "px";
    ddDiv.style.visibility = "visible";
    ddIdTarget = idTarget;
    ddGuidTarget = guidTarget;
}

Several sites I searched suggested overflow:auto and setting a size, but that didn't seem to change anything. Maybe I messed up setting the size? But it seems to me a fixed size wouldn't help as the available space depends on which row is being entered. I found that overflow:scroll gave me horizontal and vertical scroll bars, but the bars just extend with the div right off the end of the screen. I've considered trying to figure out how much room is left, and making the div appear high enough to fit, but I think that would be ugly. I'll go there if I have to though.

Any tips, help, pointers to ideas would be very appreciated. Thanks.

like image 316
Shane Avatar asked May 05 '15 23:05

Shane


1 Answers

This is a common problem when writing expanding widgets.

You simply need to

  1. Set a desired max-height to the element;
  2. set overflow-y: auto to instruct it to add scrollbars only when needed;
  3. with javascript, calculate if the top position of the element plus the height of the element is higher than the height of the viewport (the visible area of the window);
  4. if it is overflowing, then do something. Generally, the usual behavior is to invert the direction of the dropdown, making it start from the bottom of the parent element, growing toward the top.

Take a look at the jsFiddle example made using jQuery (for convenience). Resize the window, and see what happens to the overflowing element.

Or just run the following code snippet:

$(".dropdown li").on('mouseenter mouseleave', function(e) {
  var submenu = $('ul:first', this);
  var submenuTop = submenu.offset().top;
  var submenuHeight = submenu.height();
  var viewportHeight = $(window).height();

  var isOverflowing = (submenuTop + submenuHeight > viewportHeight);

  if (isOverflowing) {
    submenu.css("top", $(this).height() - submenuHeight);
  } else {
    submenu.css("top", 0);
  }
});
* {
  box-sizing: border-box;
}

body {
  background: dodgerBlue;
}

.dropdown,
.dropdown li,
.dropdown ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.dropdown {
  width: 100%;
}

.dropdown ul {
  position: absolute;
  top: 100%;
  left: 100%;
  visibility: hidden;
  display: none;
  z-index: 1;
  width: 200px;
  max-height: 150px;
  overflow-y: auto;
  overflow-x: hidden;
}

.dropdown li {
  position: relative;
  float: left;
  clear: both;
}

.dropdown li:hover {
  z-index: 910;
}

.dropdown ul:hover,
.dropdown li:hover>ul,
.dropdown a:hover+ul,
.dropdown a:focus+ul {
  visibility: visible;
  display: block;
}

.dropdown a {
  display: block;
  background: #000;
  color: #fff;
  padding: 5px 20px;
  text-decoration: none;
}

.dropdown ul li {
  width: 100%;
}

.dropdown li:hover a {
  background: #333;
}

.dropdown li a:focus,
.dropdown li a:hover {
  background: #666;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<ul class="dropdown">
  <li><a href="#">Link 1</a>

    <ul>
      <li><a href="#">Link 1</a>

      </li>
      <li><a href="#">Link 2</a>

      </li>
      <li><a href="#">Link 3</a>

      </li>
      <li><a href="#">Link 4</a>

      </li>
      <li><a href="#">Link 5</a>

      </li>
    </ul>
  </li>
  <li><a href="#">Link 2</a>

    <ul>
      <li><a href="#">Link 1</a>

      </li>
      <li><a href="#">Link 2</a>

      </li>
      <li><a href="#">Link 3</a>

      </li>
    </ul>
  </li>
  <li><a href="#">Link 3</a>

    <ul>
      <li><a href="#">Link 1</a>

      </li>
      <li><a href="#">Link 2</a>

      </li>
    </ul>
  </li>
  <li><a href="#">Link 4</a>

    <ul>
      <li><a href="#">Link 1</a>

      </li>
      <li><a href="#">Link 2</a>

      </li>
      <li><a href="#">Link 3</a>

      </li>
      <li><a href="#">Link 4</a>

      </li>
      <li><a href="#">Link 5</a>

      </li>
      <li><a href="#">Link 6</a>

      </li>
      <li><a href="#">Link 7</a>

      </li>
      <li><a href="#">Link 8</a>

      </li>
    </ul>
  </li>
  <li><a href="#">Link 5</a>

    <ul>
      <li><a href="#">Link 1</a>

      </li>
      <li><a href="#">Link 2</a>

      </li>
      <li><a href="#">Link 3</a>

      </li>
      <li><a href="#">Link 4</a>

      </li>
      <li><a href="#">Link 5</a>

      </li>
    </ul>
  </li>
  <li><a href="#">Link 6</a>

    <ul>
      <li><a href="#">Link 1</a>

      </li>
      <li><a href="#">Link 2</a>

      </li>
      <li><a href="#">Link 3</a>

      </li>
      <li><a href="#">Link 4</a>

      </li>
      <li><a href="#">Link 5</a>

      </li>
    </ul>
  </li>
  <li><a href="#">Link 7</a>

    <ul>
      <li><a href="#">Link 1</a>

      </li>
      <li><a href="#">Link 2</a>

      </li>
      <li><a href="#">Link 3</a>

      </li>
      <li><a href="#">Link 4</a>

      </li>
      <li><a href="#">Link 5</a>

      </li>
      <li><a href="#">Link 6</a>

      </li>
      <li><a href="#">Link 7</a>

      </li>
      <li><a href="#">Link 8</a>

      </li>
    </ul>
  </li>
</ul>
like image 148
Andrea Ligios Avatar answered Nov 06 '22 00:11

Andrea Ligios