document.querySelector(".circle").onclick = function() {
popover = document.querySelector('.popover');
bounding = document.querySelector('.popover').getBoundingClientRect();
convbound = document.querySelector('.conversation').getBoundingClientRect();
circle = this.getBoundingClientRect();
offset = circle.top - convbound.top;
loffset = circle.left - convbound.left;
popover.style.visibility = "visible";
popover.style.top = offset + circle.height + "px";
popover.style.left = loffset + circle.width/2 - bounding.width/2 + "px";
document.querySelector('.conversation').appendChild(popover);
}
html,
body,
.col-6-center {
height: 100%;
}
body {
overflow: hidden;
margin: 0;
}
.main {
display: flex;
flex-direction: row;
height: 100vh;
}
.col-3-left {
display: flex;
width: 25%;
background-color: #d7fde5;
}
.col-3-right {
display: flex;
width: 25%;
background-color: #d7fde5;
}
.col-6-center {
display: flex;
flex-direction: column;
width: 50%;
position: relative;
}
.col-header {
flex: 0 0 auto;
background-color: #dcdcdc;
}
.conversation {
flex: 1 1 auto;
position: relative;
overflow-y: auto;
background-color: #fdfcd7;
}
.conv-scroll {
overflow-x: hidden;
}
.col-footer {
flex: 0 0 auto;
background-color: #dcdcdc;
}
.circle {
width:20px;
height:20px;
background-color:red;
border-radius:50%;
cursor: pointer;
float:right;
}
.popover {
display:block;
visibility:hidden;
width:250px;
height:20px;
position: absolute;
background-color: black;
color: white;
border-radius:10px;
border:2px solid blue;
padding: 5px;
z-index:100;
}
<div class="main">
<div class="col-3-left">left sidebar</div>
<div class="col-6-center">
<div class="col-header">sticky column header</div>
<div class="conversation">
<div class="conv-scroll">
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....<span class="circle"></span></p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
<p>Chat messages....</p>
</div>
</div>
<div class="col-footer">sticky column footer</div>
</div>
<div class="col-3-right">right sidebar</div>
<div class="popover">popover clipped content</div>
</div>
I’ve spent hours on StackOverflow/Google trying to fix this, but my case seems to be a bit more complicated than the solutions here, which are not working for me. I’ve attached an example layout of my page below. Basically it’s three flex, scrollable columns layout for a chat room.
Each of the three columns are supposed to be scrollable. The center column displays the chat messages, along with small chat info header at the top, and a small input at the bottom to write messages.
The inner portion (class .conversation and it’s child .conv-scroll), I want it to be scrollable vertically but not horizontally (overflow-y:auto; overflow-x: hidden
). I’m not exactly sure which elements these get applied to.
The part I cannot figure out: If a user clicks the red circle reaction, a JavaScript-positioned popup is supposed to appear listing the names of the users who reacted to the message. That popup keeps getting clipped on the right side by the scrollbar.
My intention: I want to pop-over to display above the scrollbar and part of the right-hand column, so it’s not getting clipped. It needs to be appended to center .conversation/.conv-scroll column, so that when a user scrolls through the messages, the popover scrolls along with it.
Things I’ve tried that don’t quite work: I tried appending the popover to the <body>
and allow the body to be scrollable, but that just puts a huge scrollbar on the right-side of the browser. I would prefer the scrollbar just being inside that center column.
I also tried using the position:fixed
on the popover, but that is a no-go, because the popup doesn’t scroll along with the conversation.
I tried one of "popular tricks" solution on StackOverflow involving putting a position:relative
on the parent, and putting overflow-x:hidden
on the conv-scroll messages container, but that isn’t working for me for some reason:
<div class='conversation'>
<div class='conv-scroll'></div>
<div class='popover'></div>
</div>
.conversation {position:relative;}
.conv-scroll {overflow-x:hidden;}
.popover {position:absolute;}
Perhaps this doesn’t work because the center column is sort of "fixed", but is scrollable inside or it was for a different use case? I don’t know. This is one of the most difficult website design problems I’ve come across, but I’ve seen it work on other sites like facebook, though probably with different CSS rules. I’m hoping someone with vastly more CSS experience than me knows the right way to do this.
EDIT / UPDATE #2:
I added an interactive script, including Javascript to view this live. Click the red circle to see the popup.
Note that the ".conv-scroll" element may or may not be needed, I only added that as an extra container to try unsuccessfully to get one of the other solutions to work.
I do need the overflow-x: hidden;
working correctly on that center column, to prevent a horizontal scrollbar from appearing with these chat messages. Lastly, I need any possible way to get that popover to show up fully, above the vertical scrollbar and the right column, rather than being clipped. It also needs to scroll along with the chat messages when the user scrolls.
2
Answers
Updated my answer, which I think matches your screenshot above. As you’re noticing, the problem is trying to place something in an overflow container but not have it affected by overflow. So what I’m doing here is finding the position of the clicked
.circle
, and using its coordinates to position the.popover
below it. The important part is that the.popover
lives outside of the container withoverflow: hidden;
. As the.conversation
container is scrolled, the coordinates of the.circle
are tracked and continually update the position of the.popover
to follow up or down the screen. I’m using a class.active
to toggle it’s visibility, and using adata-pos
attribute on.popover
to track which circle was clicked and whether it needs to show or hide. I also adjust the z-index on the column header and footer so the.popover
scrolled underneath it, but it’d probably be cleaner to just fade out the.popover
just before it goes under the header or footer.You’ll also notice I wrote it with jQuery instead of vanilla JS, only because it’s quicker for me to get my thoughts out. Converting it to vanilla JS is straight forward though, as long as this solves your problem.
This is the output you want right?
Here is the code using which i achieved this
I added a new parent called test in the HTML outside of the class conversation and inserted both popover and circle inside it, I set the position of #test as absolute which is still relative to the main class, and also removed some lines from the javascript like changed the location where you were appending popover from conversation to #test and removed other lines which you were using to calculate the location of where to show popover, what you were doing wrong was that you were calculating it dynamically instead I set it absolute in the middle of the page and this way it is not going anywhere and fixed there and so is the popover.
Let me know if there is something wrong with my answer.