-6

I am working on an already existing project, with a pretty complex front end. I want to introduce a new element which should be a substitute for a dropdown. Basically it's a div bind with knockout to a collection.

The problem I have is that on a single page there are several divs inside which a more complex structure is rendered for each one, and inside one of this divs is my custom dropdown. The problem is that when I try to expand the dropdown (a class bind to a click event using jQuery) my "dropdown" is rendered up to the top of the div because of the fact that there is too much content and in order to preserve the entire page look and appearance there is no good way to use overflow: visible.

A snippet that pretty well introduce my problem is HERE :

$('.show-dropdown').click(function() {
if ($(this).next('.render-this').hasClass('hide-me')) {
 $(this).next('.render-this').removeClass('hide-me');
  } else {
  $(this).next('.render-this').addClass('hide-me');
  }

})
td {
  position: relative;
}

#top-div {
  width: 500px;
  max-width: 500px;
border: 1px solid black;
  max-height: 100px;
  overflow-y: auto;
white-space: nowrap;
}

#bottom-div {
  width: 500px;
  max-width: 500px;
border: 1px solid black;
  max-height: 100px;
    overflow-y: auto;

}

.show-dropdown {
  width: 120px;
  height: 40px;
  background-color: green;
}

.render-this {
  position: absolute;
  bottom: 10px;
  z-index: 5;
  width: 20px;
  height: 150px;
  background-color: red;
}
.hide-me {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="top-div">
<p>
lorem ipsum lorem ipsum lorem ipsum lorem ipsumlorem ipsumlorem ipsum lorem ipsum lorem ipsum
</p>
</div>
<div id="bottom-div">
<table class="w3-table">
<tr>
  <th>Column 1</th>
  <th>Column 2</th>
  <th>Column 3</th>
  <th>Column 4</th>
  <th>Column 5</th>
  <th>Column 6</th>
</tr>
<tr>
  <td><div class="show-dropdown"></div><div class="render-this hide-me"></div></td>
  <td><div class="show-dropdown"></div><div class="render-this hide-me"></div></td>
  <td><div class="show-dropdown"></div><div class="render-this hide-me"></div></td>
  <td><div class="show-dropdown"></div><div class="render-this hide-me"></div></td>
  <td><div class="show-dropdown">></div><div class="render-this hide-me"></div></td>
  <td><div class="show-dropdown"></div><div class="render-this hide-me"></div></td>
</tr>
</table>

I've read a lot about this topic. My conclusion so far is that if you have overflow it is pretty much game over. However from the question HERE I see that with transform and maybe some other CSS it might be still possible to achieve something. Also, what I need is to render my dropdown completely, I was also thinking about using overflow: visible and some sort of JS created scroll, but haven't dig deep for this still.

Rich
  • 1,567
  • 14
  • 24
Leron
  • 9,546
  • 35
  • 156
  • 257
  • 8
    *"A snippet that pretty well introduce my problem is HERE"* Your whole question (including any necessary code or examples) has to be **in** your question, not just linked. Two reasons: People shouldn't have to go off-site to help you; and links rot, making the question and its answers useless to people in the future. If the example is important/useful enough to link, move it *into* the question. – T.J. Crowder Jun 15 '18 at 13:56
  • @T.J.Crowder I think you are right, but on several occasions I found snippets that weren't working as part of the question, but at least for the near future I know that the `JsFiddle` will work. That's the reason why I prefer this approach. It seems to me that this feature is not working good enough YET for SO. I might not be right, but this is my impression. – Leron Jun 15 '18 at 14:01
  • 5
    Links to jsfiddle **MUST** be accompanied by code in the question itself – Pete Jun 15 '18 at 14:02
  • @Pete I won't even make the effort to copy-paste all the links from SO, regarding just this topic with a lot less explanation and code! Feel free to downvote and/or close, I still think that this is the best way to represent my question if anyone is interested in helping. With your rating you should know that `DRY` is usually a good pattern and I don't see how I can add some extra code along with the one already in the fiddle. – Leron Jun 15 '18 at 14:07
  • 4
    @Leron - That's fine, don't make it a snippet if you can't make it work. But the code must be in the question (even if just as a code block). *Also* linking to jsFiddle is okay. (But it's been down or slow often enough that I would try to get the snippet working. Unless you need ajax, forms, or local storage -- sadly, those are limitations on snippets.) – T.J. Crowder Jun 15 '18 at 14:08
  • 5
    I'm just saying it is a rule of SO that comes up as a big red box. if you don't want to follow the rules then that's fine - SO is meant to be a repository that will help future users, this question will be of no help if the jsfiddle link becomes dead in future. At least if you put the code in your question, people can still see what wasn't working, even if you don't have a working snippet – Pete Jun 15 '18 at 14:10

3 Answers3

1

You can add a wrapper div with relative position around the scrollable container and then your absolute positioned dropdown would bind to that element. As you can see in this example.

css:

.position-relative {
    position: relative;
}

#top-div {
    width: 500px;
    max-width: 500px;
    border: 1px solid black;
    max-height: 100px;
    overflow-y: auto;
    white-space: nowrap;
}

#bottom-div {
    width: 500px;
    max-width: 500px;
    border: 1px solid black;
    max-height: 100px;
    overflow-y: auto;

}

.show-dropdown {
    width: 120px;
    height: 40px;
    background-color: green;
}

.render-this {
    position: absolute;
    bottom: 10px;
    z-index: 5;
    width: 20px;
    height: 150px;
    background-color: red;
}
.hide-me {
    display: none;
}

html:

<div class="position-relative">
    <div id="bottom-div">
        <table class="w3-table">
            <tr>
                <th>Column 1</th>
                <th>Column 2</th>
                <th>Column 3</th>
                <th>Column 4</th>
                <th>Column 5</th>
                <th>Column 6</th>
            </tr>
            <tr>
                <td>
                    <div class="show-dropdown"></div>
                    <div class="render-this hide-me"></div>
                </td>
                <td>
                    <div class="show-dropdown"></div>
                    <div class="render-this hide-me"></div>
                </td>
                <td>
                    <div class="show-dropdown"></div>
                    <div class="render-this hide-me"></div>
                </td>
                <td>
                    <div class="show-dropdown"></div>
                    <div class="render-this hide-me"></div>
                </td>
                <td>
                    <div class="show-dropdown">></div>
                    <div class="render-this hide-me"></div>
                </td>
                <td>
                    <div class="show-dropdown"></div>
                    <div class="render-this hide-me"></div>
                </td>
            </tr>
        </table>
    </div>
</div>

js

$('.show-dropdown').click(function() {
    if ($(this).next('.render-this').hasClass('hide-me')) {
        $(this).next('.render-this').removeClass('hide-me');
    } else {
        $(this).next('.render-this').addClass('hide-me');
    }
})

Then you can adjust the top, left, right, bottom properties as you wish.

Gregor Ojstersek
  • 1,369
  • 7
  • 12
1

If you change your CSS as follows, you can get this to work as you would like:

  • Remove the position:relative; from your td.
  • Use a different approach to make the #bottom-div expend to it's content, using display: table;, and also apply the position:relative; to it.
  • all other css rules stay the same.

So, this is your new css:

#top-div {
  width: 500px;
  max-width: 500px;
border: 1px solid black;
  max-height: 100px;
  overflow-y: auto;
white-space: nowrap;
}

#bottom-div {
  width: 500px;
  max-width: 500px;
  border: 1px solid black;
  max-height: 100px;
  position: relative;
  display: table;
}

.show-dropdown {
  width: 120px;
  height: 40px;
  background-color: green;
}

.render-this {
  position: absolute;
  bottom: 10px;
  z-index: 5;
  width: 20px;
  height: 150px;
  background-color: red;
}
.hide-me {
  display: none;
}

Here is a link to a Fiddle with the new css.

Or A.
  • 1,220
  • 1
  • 15
  • 28
  • Thanks for the suggestion. I haven't test it in the real scenario, but at least from the fiddle I'm concluding that once the content becomes too wide the overflow will be applied to the whole page which is not the desired behavior. Once the width of the **td** overflows I want only the wrapping div to get scroll, so you can scroll only this particular content and not the entire page. – Leron Jun 20 '18 at 14:08
  • It seems there is more info in order to help you solve this issue. Can you further tell me your constraints? I can only help you based on the info you provided in the question. – Or A. Jun 20 '18 at 14:26
  • Basically I want to render the div over the scrollbar. That's it. By this point I'm only interested at this. Gonna edit the question later too to calrifay that. The bounty is marked as due to outdated responses, because from questions several years old I understood that it was impossible to do this. However I'm wondering if as of 2018 this is still the case. – Leron Jun 20 '18 at 14:44
  • So if you only want to render the div over the scrollbar, both answers do that - in different ways, but in the first comment you mentioned a constraint - 'Once the width of the td overflows I want only the wrapping div to get scroll'. If it is not important to you, that's ok, but be consistent about the requirements you have. As you can see, this is possible. – Or A. Jun 20 '18 at 15:00
  • I'll be honest with you. After this question was that heavily downvoted I don't expect much. However see what would happen in your solution if we add more content : https://jsfiddle.net/j1sawk9e/5/ And what is expected according to the fiddle I've posted: https://jsfiddle.net/mg8zbr41/128/ And now - if you think there is no difference and that I'm confusing you then.. I really don't know what else to write in order to make it more clear. – Leron Jun 20 '18 at 19:19
  • What happens in the updated fiddle with my solution you posted above (https://jsfiddle.net/j1sawk9e/5) is, since **the original HTML you posted was missing a closing tag** for the `#bottom-div`, the added HTML you inserted ended up inside the original `#bottom-div`, that according to it's CSS, *SHOULD* do exactly what it does. Here is a fixed version of your addition to my answer: https://jsfiddle.net/j1sawk9e/10/. – Or A. Jun 21 '18 at 08:06
  • The forgotten `
    ` was a typo. I apologize for that. However I'm ending this discussion because I don't see any progress. Just a last attempt to explain what is the issue, just read what the last paragraph is telling you - https://jsfiddle.net/j1sawk9e/14/
    – Leron Jun 21 '18 at 08:23
1

Demo

https://jsfiddle.net/mg8zbr41/172/

(I hope this was the desired behaviour)

Explanation

The whole problem would have been solved if we were allowed to have overflow-x: auto and overflow-y: visible together. But we cannot do that (see this answer). So we use the following workaround.

  1. If you want to have want the red div to pop out you cannot put position: relative parent. So we remove that first
  2. Now bottom becomes relative to parent relative element i.e. body but we don't want that so we also remove bottom
  3. Now we have top, right, bottom, left all as auto. So the elements placed below the green box as it would have been if it was static. The only difference is it pops out of the bottom box
  4. Now we want it to be 10px above the green box for that we use translateY(calc(100% + 10px) * -1)
  5. Now this works till there's no scrolling. When the div is scrolled the red box stays there and doesn't move with its green box, we need to fix that
  6. This can be easily fixed if we know how much is the div scrolled. Suppose the div is scrolled by 100px towards left we'll shift the red box towards left by 100px
  7. We cannot find scrollLeft without JS. I personally don't like JS intervention for styling. We cannot avoid it but at least we can make it more semantic by using css variables for communication between JS and CSS.
  8. We use JS to update a --scroll-left css variable on #bottom-div with the scrollLeft value
  9. Once we have --scroll-left we can now add translateX(calc(var(--scroll-left,0px) * -1))
  10. Also we don't want the red box to pop out of the box horizontally. We cannot fix this by overflow: hidden because that would require position: relative. So we use clip-path: inset(-999px 0px -999px 0px).
  11. Finally we achieved want we wanted. Phew.

Demerits:

  1. Horizontal repositioning will be laggy in Firefox because of Scroll Lined Effects. Same might be the problem in mobile browsers

See also:

https://css-tricks.com/popping-hidden-overflow/ Source of inspiration for my answer but both (solution in the article and my solution) are quite different but same core approach

Devansh J
  • 4,006
  • 11
  • 23