How to shift statically positioned elements over?

jquery

#1

Hi! I have a container div, let's call it "port." Inside port are multiple divs (let's call them "items") that overflow past what the size of their parent div (port) is. When one of the items is clicked, I want to shift its right edge to be at the same location that the right edge of port is. However, I also want all the other items to shift accordingly when the clicked item moves, so I can't use position: absolute for the items.

If that text description doesn't make sense to you, maybe these visuals will help:

Normally the items overflow past the edge of the port.

When item1 is clicked, it "snaps" to be fully in the port (AKA its right edge becomes the same as port's right edge). All other sibling items in front of/behind it adjust accordingly.

What is the simplest way to do this?
I've thought about using absolute positioning, flexboxes, and other stuff but nothing seems to work. I'm a bit stumped on how to do this. Thank you very much in advance!


#2

Hi,

I think you may need to provide a demo of what's happening as I have a hard time understanding why you would need to do something like this.

Why do they overflow? Are they too big for the div?
What is the purpose of allowing them to overflow. Overflow should always be controlled.

Why can't you align them like that to start with. Surely if they are going to fit they will fit to the right normally.

If they don;t actually fit and ae too big for the div and you shift them to the edge then that means you cut off the left side of the element because surely it must stick out at the left now and have no means of scrolling to see it!

I think you need to make a reduced codepen or similar showing the behaviour you are experiencing.

It sounds to me as though you are just doing it the wrong way to start with :slight_smile: (but I may be wrong)


#3

I'm confused as well. It sounds like you want something like a slideshow but for text in divs instead of images.

I have never seen anything like how I imagine this might be. Kind of like a "book reader" where only a page and a half is visible at a time. eg. the "left page" and the left half of the "right page" is visible until the half page is clicked at which point the display shifts to show the right half of the "left page" and the "right page".

Typically, this kind of "more" is done with accordions, not scrollers. But if this is what you had in mind I guess it might work even though seeing partial sentences as opposed to complete beginning sentences feels like it could be more confusing. And I'm not sure how getting to "page 3" (your item2) would be done.

Or I'm completely off base and don't understand your goal.


#4

I didn't think of that :slight_smile:

In that case JS would be needed to work out how much the current box overlaps and then use translate to shift the whole row left.


#5

Ah, sorry, I will construct a Codepen to show what I mean! Everything that overflows from the port cannot be seen, and all of the items have the same height as the port. This is most definitely controlled, and the result is a bunch of tabs that you can scroll left and right in can only see a few at a time. Maybe that was a bit more clear, but if not I'll get the Codepen up as soon as I can.
Sorry for making this confusing! I (think?) I'm doing this the right way.


#7

Alright here is a minimum jsfiddle of the challenge: http://jsfiddle.net/xpvt214o/289937/

I souped up some HTML and CSS that gives a general idea of what my actual implementation of this concept is like. It's a sliding set of tabs. All of the JavaScript is implemented differently in the real code, there aren't specific ids that are being listened for as there are here, but this makes the jsfiddle work.

In the real implementation the tabs also scroll left and right just fine using a plugin that makes them act like a touchscreen (these jsfiddle ones technically scroll fine if you're using a touchscreen, actually), but sorry that with a mouse the jsfiddle doesn't allow you to scroll because the scrollbar is hidden. You just have to use highlighting to move the tabs back and forth.

One more time trying to explain in text. Read this as you look at the jsfiddle
So there are a set of side-by-side tabs. The user can click on a tab to highlight it (and display stuff underneath, but that is irrelevant to this jsfiddle). If the tab they click is not fully visible (AKA it is not scrolled over enough so part of it is not within the port and it looks cut off), then I want the tabs to all scroll over so that clicked tab is fully visible (have them all shift over enough so the right/left edge of the clicked tab matches up with the port's right/left edge).

Hopefully this makes more sense now!? :grimacing: Thanks for being so patient, guys!


#8

And if you're navigating by keyboard? As far as I can see, the tabs are currently completely inaccessible in that case.


#9

This is purely a programming task so I have moved the post to the JS forum so that one of the JS gurus can help.:slight_smile:

As I see it you will still need to clarify the behaviour because there are some use-cases that will be hard to resolve.

1) If the content in the tab is wider than the available block (too much text to display) then it will be impossible to select another tab. How are you going to ensure that you never have tab content wider than your port size (especially in a responsive layout)?

2) If a tab is fully visible in the viewport already (such as the second tab) then what happens when you click it? Does it then move to the right edge of the block?

3) If the tab aligns to the right edge how can you select another tab that may be off-screen to the right?

4) You say if the tab is too wide to align to the right then align to the left. That will bring us to the scenario in point 1 above where you will no longer be able to navigate to any other tab.

I don't think you have thought through all the possible implications which you need to do before programming can begin. Usually for a slideshow effect like this you would simply have left and right arrows at the edge of the screen to advance to the next or previous tab as required which will be a much more logical approach. As it stands you will reach situations where you cannot progress to the next tab unless you can define how that will work.

Yes keyboard control should also be integrated into the solution to be accessible and of course the element should be accessible if js is disabled and only enhanced when js is present.


#10

Thank you for your concerns! However I see there is still a misunderstanding about what this is. This is not any sort of slideshow meant to show information. This is horizontal set of tabs that display one piece of content beneath them. Which piece of content is shown is dependent upon which tab is currently selected. Hopefully with that in mind these responses to your concerns make sense:

  1. The tab content will never be wider than the available block because in the actual implementation the only text that can be in the tabs is selected from a drop-down list. This isn't a use case where users are given the freedom to type whatever they want into the tabs.

  2. If the tab is already fully visible in the viewport and is clicked, nothing happens. That is why I have that if (part of the tab is not visible in the port) check in my "pseudocode" within the JavaScript function.

  3. You can select one that is more to the right by scrolling. Like I said, this simple jsfiddle just doesn't implement the scrolling like the real deal does.

  4. Ah, no, sorry, I meant that it aligns to the left if the tab you are clicking is overflowing off the left side and thus part of its left side is not visible. There is no scenario where a tab is large enough to overflow over both the left and right side (or even take up the whole viewport) and I do not intend to add one.

This will not be a slideshow or a carousel, nothing is moving on its own. The text in the tab does not need to be fully visible at all times, only when the user clicks on that tab. This is just a small widget that has a variety of tabs for the user to select from, and beneath the tabs (still in the widget) there is some content they will see. Only one piece of content will be displayed below at a time, and which piece of content is being displayed is based on which tab is currently selected.

And as I said in my response to @TechnoBear, you're definitely right about keyboard accessibility, I will implement that afterward. Right now, it doesn't apply to the mouse use-case, though.


#12

Ah, you're right! I didn't even think about that! I will definitely have to make this keyboard accessible, and I will do so. Thanks for pointing that out!
That does not affect the issue of having the tabs move over when clicked, though, unless I implement whatever solution we find here to also do that when the user uses the keyboard switch between tabs.


#13

There seems to be some confusion over what the use case of this is, so let me clarify a bit. This is not any sort of carousel or slideshow. These are tabs that are used to display one only one piece of information at a given time. It is something more similar to the window below:

This is just a quick random image I found online, but it gives a more general idea of the kind of thing I am going for. See how when the "Program" tab is selected, it shows the information associated with "Program". When "SSL" is selected it switches the content below to show the information associated with "SSL". Notice how the tab starting with a "D" is not fully visible. What I am trying to do (in my in my analogous scenario) is make it so that when the "D..." tab is clicked, it comes fully into view and shifts all of the rest of the tabs over to the left. I only need everything to shift to whatever is the minimum amount required to make the "D..." tab completely readable, that is why I want to align its right edge with the right edge of the rectangle that surrounds all the tabs.

Hopefully this makes things a lot more clear!


#14

Why don't you just have JS add/remove a .current class to the item you that is selected from the dropdown list.

Then set the current class as position absolute to remove it from the flow and position it in the viewing area.

Here is an example with the .current class hard coded in Tab2

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Test Page</title>
<style>
html {
	box-sizing: border-box;
}
*, *:before, *:after {
	box-sizing: inherit;
}
#TabsRow {
	white-space: nowrap;
	box-sizing: content-box;
	position: relative;
}
.Tab {
	display: inline-block;
	border-bottom: black 2px solid;
	width: 100%;
	text-align: center;
}
.current {
	position: absolute;
	top: 0;
	left: 0;
	z-index: 1;
	background: red;
}
#port {
	width: 250px;
	height: 84px;
	border-left: black 2px solid;
	border-right: black 2px solid;
	border-top: black 2px solid;
	overflow-x: auto;
	white-space: nowrap;
	overflow-y: hidden;
}
#portInterior {
	height: 94px;
	overflow-x: auto;
	white-space: nowrap;
	background-color: red;
}

</style>

</head>
<body>

<div id="port" style="height:50px;">
  <div id="portInterior">
    <div id="TabsRow">
      <div class="Tab" id="Tab1">Tab 1 Content</div>
      <div class="Tab current" id="Tab2">Tab 2 Content</div>
      <div class="Tab" id="Tab3">Tab 3 Content</div>
    </div>
  </div>
</div>

</body>
</html>

#15

If you can select the next tab by scrolling then why do you actually need to move the tabs along? (and how are you scrolling if there is no scroll mechanism? mousewheel or swipe or ?). Anyway wouldn't the user just scroll it into view as you have mentioned a couple of times now.

I'm still finding it hard to see a proper use-case for this. Imagine your tab is right aligned to start with. How would a user know that there are more tabs hidden out of sight unless there was a scrollbar to show there were more items or there were arrows available (as in the picture you just showed). A user would not think to scroll if they couldn't see any reason to do so.

I'm sure it is quite possible in JS to simply detect the edge of the element and its parent and move the whole line into view but if you are at the same time using some sort of script to scroll the non-scrollable div then obviously these two must tie in together. If for example the div was just moved using translate then the normal scroll action would no longer show all content. If your script is using some sort of horizontal scroll into view then clicking the tab needs to use the same method to keep it all in sync.

Maybe I am making this sound more complicated than it is :slight_smile:


#16

I can understand the concept of having different content occupy the same screen real estate. The changes are typically controlled by pseudo-tabs, left / right arrows, links or buttons, or scroll bars.

I have not seen any controlled by clicking on peripheral partial content. Not that one needs to adhere to established norms or shouldn't explore different approaches. Who knows it might "catch on" and become widely adopted.

I see two concerns. Not only would it need to work, but it would need to be intuitive enough that users knew how it worked.

Personally, I think if the different content could not easily have descriptive "labels" that could work in pseudo-tabs, I would go with either a scrollbar in the containing element or left and right arrows because they are relatively common and users would know how to use them without needing to think about what they were for.


#17

If you're using overflow: scroll (or auto) on the "port", you can directly change its scroll position by setting its .scrollLeft property. Here's a pen:


#18

@Ray.H: The reason I don't just use position:absolute and remove it from the flow is because then the rest of the items wouldn't scroll over as well because they are all positioned statically.

@PaulOB In the sceneraio where this is used, the user already knows that there are more tabs, so them being hidden and the user not knowing they are existing is not an issue. While I really do appreciate your concern in making sure I'm using the correct approach here, I also did come here hoping to get an answer and not just to get criticized on what the use case scenario might be, so a little trust in my judgement that this is the right type of ineraction for the actual implementation I am using it in would be great :slight_smile: Anyway, I guess this is all moot now because @m3g4p0p looks like he has provided exactly what I was looking for.

@Mittineague I'm pretty sure that in this use case the user would know where the tabs are and if there are more tabs to be scrolled to because where I am using this they know exactly where the tabs are supposed and which tabs are supposed to be there. It makes sense if you see it in its full environment.


#19

Oh, duh! That makes perfect sense, of course! Thank you SO much this is exactly what I was looking for!

Your pen is the exact thing I was imagining in my head, to a tee.


#20

Hi, Yes I understand that now and knew that's what you were asking all along.

I failed to understand what was going to trigger the auto scrolling, you mentioned a dropdown list so I thought that was going to be the call to action for bringing the item into view.

In regards to the working codepen demo, It took me a while to figure out that I needed to click on an item to auto scroll it completely into view. But it looks like you have to do some manual scrolling to get another item into view in order to click on it.

Glad you got it sorted out :slight_smile:


#21

Sorry if it sounded like criticism but really was meant to be helpful concern and clarification.

Glad you got it sorted anyway.:slight_smile:


#22

It was still helpful and I do appreciate it, I'm not upset or anything - we were just weren't ever getting to answering "how do we implement this approach" but instead were continuing to spend a lot of time asking "is this the right approach" (which usually is a good thing but eventually when all the angles are considered there is a point when you can finally move on to trying to implement it).

I do sincerely appreciate all your time spent though, and I don't want to come off as ungrateful! So thanks again!