Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
During the initial development cycle of #1137920: Fix toolbar on small screen sizes and redesign toolbar for desktop, it was noted that the behavior of the overlay in relation to the Toolbar is not ideal. When the toolbar tray is oriented vertically, it's not yet known what the ideal reaction of the overlay should be.
Overlay over the vertical toolbar tray.
Proposed resolution
None yet.
Remaining tasks
None yet.
User interface changes
Yes there will be.
API changes
None expected.
Related Issues
Followup: #1958246: Update JS documentation to follow Doxygen format (remove curly braces)
Original report by jessebeach
Comment | File | Size | Author |
---|---|---|---|
#91 | overlay-toolbar-overlap.jpg | 45.68 KB | damontgomery |
#76 | interdiff.txt | 10.81 KB | nod_ |
#75 | core-js-toolbar-displace-1847084-75.patch | 32.75 KB | nod_ |
#70 | Screenshot_3_24_13_2_35_PM.png | 72.83 KB | jessebeach |
#70 | Screenshot_3_24_13_2_35_PM.png | 77.87 KB | jessebeach |
Comments
Comment #1
moshe weitzman CreditAttribution: moshe weitzman commentedI floated a proposal that seems to have good initial support. I propose that after we get inline editing, we move Overlay to Contrib. The main use case for overlay will be handled in a better way.
Comment #2
Shyamala CreditAttribution: Shyamala commented+toolbar-followup
Comment #3
Bojhan CreditAttribution: Bojhan commentedShouldn't it just go to the right of the vertical toolbar? I just see a bug here.
@moshe His suggestion to me feels, out of scope and not related to this bug but simply a feature request.
Comment #4
xjmYeah, the toolbar being stuck under the overlay in when it's vertical is simply a bug, and "kill overlay" is out of scope for fixing it. :) (Also, inline editing doesn't work as the response for clicking a contextual link doodad to configure something, so -1.)
Comment #5
nod_The kill switch for overlay on screens smaller than 640px is broken. The toolbar behavior executes after the overlay behavior and messes things up. if the Overlay behavior is after the toolbar's it works. Haven't dug into why.
Comment #6
ry5n CreditAttribution: ry5n commentedIt appears that the toolbar is conflicting with the JS that disables the overlay for screen sizes less than 640px. Reloading an overlay URL with the viewport < 640px will still load the overlay. _nod did some digging and indicated to me on IRC that it might be Toolbar behaviours running after the Overlay behaviour has been called.
Comment #7
Bojhan CreditAttribution: Bojhan commentedThat you can't use vertical toolbar, with the overlay is a major bug - almost critical. Since you essentially can't use it anymore.
Comment #8
mbrett5062 CreditAttribution: mbrett5062 commentedMaybe I am missing something entirely here, but AFAICT the current functionality, with 'overlay' overlaying the toolbar tray is perfectly fine. I access the overlay to do something after clicking it in the tool tray, say 'appearance'. Then I do what I need and when finished close the overlay and move on to my next task. Why is this a problem? Can someone tell me why I would need access to the toolbar tray while I am in an overlay doing some administrative task. Or are we just asking for a quicker way to get out of one overlay to another?
Comment #9
carwin CreditAttribution: carwin commentedThe issue could be solved by modifying the z-index of the toolbar, which would force it above the overlay, but then we would have the reverse problem where the overlay wouldn't be accessible because it's hidden behind the toolbar.
One solution might be to go ahead and do the z-index adjustment to get the toolbar above the overlay and then add some code at this smaller breakpoint that automatically closes the toolbar tray after a selection has been made.
Thoughts?
@mbrett5062 - It's really just a usability issue. Many folks don't manually close the overlay and at this viewport width, with a touchscreen, the "x" to close the overlay is a bit difficult to land on properly.
Comment #10
mbrett5062 CreditAttribution: mbrett5062 commentedThe overlay closes upon completion of the task at hand, it is not a normal action to close it manually. If need be just change some CSS to re-size the close button on smaller screens. I still don't see a need to do anything else. An overlay is exactly what it says on the box. It overlays the page, moving attention away from the page, to concentrate on the task at hand. It works, leave it to do it's job. Just my opinion of course.
Comment #11
David_Rothstein CreditAttribution: David_Rothstein commentedIt closes automatically if you got there via a contextual link (for example), but not normally. If you are just browsing around your site doing a few administrative tasks, you really need to be able to browse around, not be forced to manually click the "close" button each time you want to go somewhere new.
http://drupal.org/node/1175694 and http://drupal.org/node/1427940 have lots of videos, etc., of people using the overlay to accomplish various tasks. Browsing around via the toolbar and closing often are very common, as I recall.
Comment #12
David_Rothstein CreditAttribution: David_Rothstein commentedIn the original issue (#1137920: Fix toolbar on small screen sizes and redesign toolbar for desktop), I believe @webchick suggested the problem with the overlay could be fixed by simply removing the toggle option for overlay users (which I think really translates to removing it for everyone). So people on wide screens would be forced to use the horizontal toolbar, and people on narrow screens (where the overlay is currently turned off anyway) would use the vertical toolbar.
I am not sure if that makes sense or not, but if you look at usage statistics of http://drupal.org/project/usage/admin_menu vs. http://drupal.org/project/usage/admin it seems pretty clear to me that people prefer horizontal toolbars, at least when their screen is wide enough (and those statistics don't even take into account people using the D7 core toolbar, which of course is horizontal too). So I'm not sure the vertical toolbar on a wide screen is a common enough use case that Drupal core needs to provide it.
Comment #13
Bojhan CreditAttribution: Bojhan commentedAll of these ideas, do not make sense - we just place the overlay next to the vertical toolbar. That's all, I dont see why we need to make it more complex.
Comment #14
LewisNymanShame on you all, upsetting Bojhan.
Patch! :-)
Comment #15
LewisNymanComment #16
David_Rothstein CreditAttribution: David_Rothstein commentedHave mercy! :)
Going off what @jessebeach wrote in http://drupal.org/node/1137920?page=1#comment-6756324 and http://drupal.org/node/1137920?page=1#comment-6757716 it seemed the more obvious approach here was going to have very complicated issues; perhaps that's not the case anymore.
The above patch seems to work pretty well at various widths; however I doubt I tested all the places where it was expected to cause trouble.
There is also a crazy issue with a detached hovering scrollbar (in Firefox at least) on the right side of the screen:
Comment #17
rteijeiro CreditAttribution: rteijeiro commentedI could confirm the detached vertical scrollbar issue of #1847084-16: Measure/track displacing elements better + provide change events for them (fix overlay + toolbar) on Chrome, Opera and Safari too.
Comment #18
dman CreditAttribution: dman commentedI can +1 that this was UI WTF for me as I was clicking around on a new install.
The buttons up the top looked like menu links (admin-menu style) and I didn't recognize them as toggles or anything. They all have different behaviours it seems.
Anyway, I had an overlay open, then thought I'd like to hit the menu again. Clicking menu didn't do anything apparent until I noticed it popping in and out in the obscured background. Confusing, looked broken, definitely needed a fix.
So far I can confirm that this patch does work much better. It's functional.
Seeing as it seems to achieve its purpose via css only, I don't expect functional issues (no unit test can be written AFAIK)
TLDR: +1
Comment #19
echoz CreditAttribution: echoz commented.overlay-element is an iframe, and in this patch it's changing % widths introduces it's own scrollbars.
Comment #20
tarekdj CreditAttribution: tarekdj commentedAttached patch fixes the problem. Tested successfully on FF, Chrome, safari, IE9 and chrome (Win 7).
Comment #21
tarekdj CreditAttribution: tarekdj commentedchanging status
Comment #22
Bojhan CreditAttribution: Bojhan commentedLooks good to me
Comment #23
jessebeach CreditAttribution: jessebeach commentedadding Spark and sprint tags
Comment #24
mkadin CreditAttribution: mkadin commentedI do thin Bojhan's solution is the best one to solve this, overlay should just appear to the right of the sidebar.
I tested this in chrome (on ubuntu). Works well for wide screens but as the window gets thin, the overlay drops down and some empty space shows up at the top right of the toolbar.
Comment #25
mkadin CreditAttribution: mkadin commentedForgot attachment
Comment #26
jessebeach CreditAttribution: jessebeach commentedThanks LewisNyman and tarekdj for the patches. I think the approach represented in the patch in #20 has legs, but as mkadin points out in #24, the CSS responsible for positioning the overlay container doesn't respond well at narrow screen widths.
The approach in the latest patch is a great start. I'd like to suggest we continue down this route and further abstract the displacement code in overlay so that overlay isn't referring so directly to toolbar dimensions, like in this example below.
A module like toolbar should be able to register that it has visual components that require displacement on the top and left/right. A module like overlay could register that it needs to be displaced on the top, left and right. In contrast, a component like sticky table headers would only be interested in top displacement.
Currently the displacement code lives in the overlay javascript. I'd like to see us generalize these behaviors to the
Drupal
object, so that screen real estate isn't managed in any one particular module.Comment #27
trawekp-1 CreditAttribution: trawekp-1 commented#26: This is a good idea but it is quite complicated. As you can see Toolbar has class on "body" that adds a left margin, if it would work like you describe then if there was a other module that would display something similar to toolbar on screen but for differeren purpose then it would have to register it's own displacement and so on..
That's great if that would work but to achieve that all those margins set on body could not be set using CSS but JS that could read from Drupal object those displacement settings.
---
According to patch in #20. I've found out that making a overlay container positioned absolute would work better. Now it is positioned relatively but this can not work.
Imagine that window height is 900px, there is no guarantee that #page-wrapper has 900px height. That means that #overlay-container might be lower than 900px from the top. That means that margin-top: -100% might not push the #overlay-container to top of the window, because it calculates the negative margin from body height, not #page-wrapper. Initial position of container depends on #page-wrapper height not on body height.
First alternative is to add max-height: 100% to #page-wrapper but we can't be sure that #page-wrapper would even exist in custom theme.
Second alternative is to add "position: relative" to body and to position #overlay-container absolutelty (as it is originally).
I'm attaching a patch based on patch from #20.
Comment #28
VVVi CreditAttribution: VVVi commentedchanging status
Comment #29
jessebeach CreditAttribution: jessebeach commented#27
Oh, I do agree. It is quite complicated! Nonetheless, I thought I'd give it a spin. We've got a few weeks to really take a hard look at integrating the features we just landed. I'd love to see us clean up some D7 cruft and make the new features play nice as well.
So I thought, what if we have a utility called
Drupal.displace
. It is responsible for finding the.data-offset-{edge}
classes, getting their dimensions and triggering an event when those values change. A module like overlay just needs to listen for that event and update its dimensions based on the values it gets from the utility. The responsibility of measuring the elements is no longer located in Overlay.By moving this code into a utility, we start to build an approach that could help us address this issue as well: #1827318: Fix tableheader placement which is broken in the current D8 ToolBar patch.
What I've got in this patch is still pretty rough. I just wanted to establish a draft framework, details to be cleaned up.
What this buys us is that overlay no longer refers to toolbar. So if in contrib later, someone builds a toolbar replacement, they don't have to override overlay as well to get the same or similar offset behavior.
Comment #30
trawekp-1 CreditAttribution: trawekp-1 commentedThis is really interesting. But do we really want to rely on classes and events? What do you think about adding a object that has displacement method and all page elements making displacement of overlay?
I think that creating new event types that should be triggered on body while resize is tricky to notice for developers. If it would be stored in Drupal object then you just open inspector and you see that there is a displacement object that stores all this stuff. I know that all those things would be documented but I guess object would be easier to understand.
*EDIT*
Sorry for my "it's complicated". I'm starting with helping with core and I though that we are looking for simple bug-fix. Obviously I prefer the way jessebeach suggested.
Comment #31
trawekp-1 CreditAttribution: trawekp-1 commentedI had big problems with creating a patch. Patch from #29 has completly different overlay code so I gave up.
I attach my vision of displace.js. "getDisplace" method is easily overrideable (if objects would be stored with ID or something).
Comment #32
jessebeach CreditAttribution: jessebeach commented#30
No need to be sorry! I agree, it is complicated. Sometimes we need complicated, sometimes we don't. I like to explore the complicated and see if we can pull something simple out of it. I'm glad you're starting with core. There are a lot of patterns and sub-systems buried in here and it just takes time to get familiar with them. I learned by working with others who have more experience than me. I like discussing options with folks to see if there's anything I haven't considered yet so if you're up for it, we can explore some options here as well.
What problems did you have with the patch in #29? Applying or creating an updated patch from the changes? Are you sure you applied the patch in #29 to the latest 8.x branch? Not the branch with your patch from #27 already applied. I just tried the patch in #29 applied to the latest 8.x commit (a3064d2020d442266b3bcdf70029775a72f67dae) and it applies cleanly.
I like using JavaScript classes, especially when we need to store state on a per-instance basis. From your patch in #31, I assume we would decorate elements like the toolbar container and the tray container with a
.displace
class. These we would create a Displace class instance for each of these elements.In your model, how do you foresee us handling these situations?
For (1), I assume that a module calls
Drupal.displace.getOffsets(true[, '{edge}'])
. I am, however, limited to the values at the precise moment I call this function, so we might run into race conditions if elements are still drawing or if the order of operations is not maintained.For resizing and displacement element dimension changes, we need a way to update interested elements that they need to reposition themselves. The best way I could come up with to do this is to dispatch an event with the most current offset values and let other modules listen for these events and react as they want.
Attaching a handler to the
document
is a way to capture bubbled events: http://www.quirksmode.org/js/events_order.html. It's also a singular clearing house for custom events. We use this pattern a lot in core, especially to pass state information between modules without those modules referring directly to each other's methods.Contextual module listening for an Edit module event
$(document).on('drupalEditModeChanged.contextual', toggleEditMode);
We always name these events with the pattern drupalPropertyEventtype.
You're right, it does require some documentation to know about these events. My personal philosophy is that contrib should be able to get by without knowing anything about these sub-systems. But when you do, you can build complex administration features that interact well with the subsystems or even completely replace them.
By going with a utility approach to managing element displacement, we'll need to address the following tasks
eventhandlerAlterDisplacedElements
is setting max widths and heights in a way that's no longer compatible with this new approach.Comment #33
trawekp-1 CreditAttribution: trawekp-1 commented1. Either a class on element or adding an instance of displace object to Drupal.settings.displace.elements
2. I'd suggest recalculating all offsets every time window resizes
3. Provide a method in displace object to recalculate offsets. This could be also resolved by custom event. Default resize event is only triggered on window.
I had newest branch 8.x. I don't know why but my overlay-parent.js had differences in lines than the one in your patch. That was strange. I guess it was my mistake, I haven't got enough time to investigate that so I attached only js file.
I cannot judge what method is better cause I'm the new one here. If you say that custom events is ok and it is used inside core then I truly belive you. I asked cause the more I understand the more I can help.
Anyway I have a question: Since data attr is only valid for HTML5 what do you suggest for XHTML and so?
Comment #34
jessebeach CreditAttribution: jessebeach commentedYou could use XHTML5 to validate the data attributes: http://www.w3.org/TR/html5/introduction.html#html-vs-xhtml
#33
Ok, cool, I think we did land on the same approach. Here's the resize handler in my patch from #29 (admittedly still very rough). It should only be triggering
drupalViewportOffsetChange
if the offset values change, not on every window resize event.And to get the offsets outside of the event layer, we have:
Also, when applying a patch, you can use the
--reject
flag to at least apply the parts that aren't in conflict, for example:git apply ~/path/to/some.patch --reject
Comment #35
trawekp-1 CreditAttribution: trawekp-1 commentedWell, what about XHTML 1.0, HTML 4 and others? Validator won't pass data attribute in those.
I agree that everything shouldn't be calculated on every window resize event but if there would be a dynamic width, like "width: 20%" then width of element would change on every window resize.
Comment #36
jessebeach CreditAttribution: jessebeach commentedWe're building to a doctype of
<!DOCTYPE html>
. I've not heard anyone mention the need to support validation of HTML4.X or XHTML 1.X.Comment #37
trawekp-1 CreditAttribution: trawekp-1 commentedHmm.. that's a suprise you're not considering compatibility with older doctypes. I won't argue with that. This is only another reason to stop using XHTML 1.x! What about percentage width?
Comment #38
jessebeach CreditAttribution: jessebeach commentedWe need to recalculate the displacement offsets on window resize, but only changes need to be broadcast. So if we compare the fresh calculations against the stored values from the previous resize, we can determine if they changed and only fire
drupalViewportOffsetChange
when they do. Sound good?Comment #39
trawekp-1 CreditAttribution: trawekp-1 commentedYeah, but in the matter of performance it would work the same as mine idea so I won't consider it as a progress. I must spent some time to find something better. We don't want to overdose with calculations while resize, especially on mobile devices with slower cpu's.
I hope it is the last thing I ask but what would happen if someone wants to change dimensions in his theme using CSS? He would have to change data attribute as well? That might not be comfortable for themers. We must think about such things if we want to do such framework like displace.
Comment #40
jessebeach CreditAttribution: jessebeach commentedThat shouldn't be an issue since resizes happen rarely on small devices where the viewport is often simply maximized and unresizable.
We should be fine there too, since we're getting the calculated dimensions of the elements to determine the displacements. As long as we get the width/height. padding and border sizes for any given box that will be considered part of the displacement value for an edge, we should be fine. The only way we could really know if an element changed size outside of a screen resize is to track DOM mutation events, but those aren't well supported at all yet. So any module that alters its view (like the toolbar and its tray) will need to call some method of displace to trigger a reassessment of the displacement values and possible trigger a
drupalViewportOffsetChange
event if they've changed. I don't like that part, but it seems hard to avoid if we can't simply monitor DOM elements for attribute changes.Comment #41
trawekp-1 CreditAttribution: trawekp-1 commentedHave you seen Ubuntu Touch? One of its features is that you can have browser on the left part of screen and something else on the right. The middle edge can be moved so window resize happens. I guess in future there will be more devices with such feature.
Hopefully all calculations are made after 'resize.drupalDisplace' so now it's not the case.
I've been thinking about improving your patch. Here are some things i thought about:
Anyway I think that .data-offset-{edge} is way better than width() or outerWidth() because it gives modules a opportunity to set offset by itself. But what would happen if we had two toolbars on the left and one would be under another? In this case calculations would be wrong. I'm aware that this example is weird but this is going to be an api not only for toolbar module so we should think about such options.
To prevent mistakes in calculations we could use something like .offset().left + .outerWidth() for left edge in example. This guarantees us that we always get proper offset no matter how elements are positioned.
Am I right with those things? I'd be happy to implement some solutions for those problems but before I'll start it's better to know if it makes any sense.
Comment #42
jessebeach CreditAttribution: jessebeach commentedI haven't seen it, but I think I know what you're talking about.
Check out this issue for a list of the followups. You're welcome to add a feature request for this idea. I like it.
#1846970: [meta] Responsive Toolbar follow-ups
Some thoughts on your suggestions:
I think I understand. What I'd like to avoid is having the Overlay ask Drupal.displace for the size of a specific element. This is an end-run around the API. Overlay shouldn't need to worry about referring to DOM elements directly or knowing anything about their classes or ids.
You're quite right, we shouldn't need to do this. The only alternative I can come up with is any module, like Toolbar, that has DOM elements that will displace other elements, will need to proactively tell Drupal.displace what elements it should take into account. I'm not thrilled about that option, because it's more brittle. Take a shot at it and let's see what your ideas look like in code.
Yes, we should be using outerWidth and taking a displaced element's offset into account as well. Calculating right and bottom displacement will take a little more code on top of that. Don't forget about RTL languages like Hebrew, too.
I think you're in a good place to start coding it up!
Comment #43
trawekp-1 CreditAttribution: trawekp-1 commentedI'm not following you, what feature request are you talking about? I got lost.
I'll do the best i can!
Comment #44
jessebeach CreditAttribution: jessebeach commentedOh, I meant if you'd like to add functionality to allow a user to resize a vertical try, you should open an issue for that.
Comment #45
trawekp-1 CreditAttribution: trawekp-1 commentedI've added TableHeader integration with displace.js. Now TableHeader listens for drupalViewportOffsetChange and as a result invokes Drupal.displace.getOffsets(true).
I did not change displace.js, as I need to get familiar with other core libraries. There is a lot of details that need to be considered to make this API stable.
Caching
I don't know if we should cache total or per-element offsets.
I think that best for performance would be per-element caching. When element triggers and event that its box has changed then we'd just adjust total offsets based on change of single element, we'd skip recalculating all elements. But I'm not sure if this is needed, because there won't be a lot of elements marked as displace element. I think it would not be more than 3. In this case there is no sense in caching offsets per-element.
So assuming that we have only a few elements then all elements providing displacements would only trigger an event to tell displace api to recalculate total offset. If total offset changes then displace api triggers "drupalViewportOffsetChange" on document then all interested modules can react on change.
Window resize
Also I think that there is no need for "resize" handler in displace.js. We should only recalculate offsets when something tells us to. Window resize is not equal to change of offsets. Modules will tell us when to recalculate offsets.
Calculations
Should we rely on data-offset-{edge}? I know it is good, because modules tells us what the offset of the element is. But on the other hand we can be sure what total offsets are only if we use element offset and it's outerWidth/Height.
If we decide to forget about data-offset-{edge} value then it would be smart to provide a way for modules to alter the calculations for specific element, as it was in the method that I've attached earlier.
Comment #46
trawekp-1 CreditAttribution: trawekp-1 commentedI've found out that there is a bug with patch from #29 and #45. When changing tray from vertical to horizontal "data-offset-left" was not changed to 0, so this resulted with wrong placement of tray on overlay.
Comment #47
jessebeach CreditAttribution: jessebeach commentedtrawekp, I'm working on an update of your patch in #46. I should have that posted today. There was some funkiness with its interaction with the Overlay that I'm smoothing out. Otherwise it looks great so far!
Comment #48
jessebeach CreditAttribution: jessebeach commentedtrawekp, this update to the patch addresses issues with the overlay and tableheader sticky tables. I also worked on the overlay styling a little so that it fits into a narrow space with more grace.
This is what I think we have left to do:
There is an example of using Testswarm to set up front end tests here: http://drupal.org/files/mobile-preview-1741498-67.patch
What do you think trawekp? Want to get this up to the line then I'll do a review? I'll try to get some other folks in here as well now that we're pretty far along with it.
Comment #49
jessebeach CreditAttribution: jessebeach commentedComment #50
David_Rothstein CreditAttribution: David_Rothstein commentedHard to get all the relevant things in the title and make them fit...
I was going to mention that this sounds a bit like the old issue at #787940: Generic approach for position:fixed elements like Toolbar, tableHeader but I see you already added a link to this one there.
Comment #51
trawekp-1 CreditAttribution: trawekp-1 commentedFinally I have some time to continue. I've noticed that all my patches were incomplete, now I know how to do this right.
I changed some styles in core/modules/overlay/overlay-child.css. I think that box-sizing is not necessary as #overlay is a block element so as a default it will have 100% of width. As long as we don't define "width: 100%;" element will have 100% including paddings (so it's outerWidth won't exceed 100%). Correct me if I'm wrong. Because box-sizing is not supported in IE8 it's better not to use it if we don't have to.
I think that caching is not needed, we should only decrease number of Drupal.displace() calls. To achive that I removed all Drupal.displace() calls, instead of that I've added an event called "drupalOffsetChange" that displace listens to and runs debounced Drupal.displace. Also I've removed a binding to resize event as it is not necessary now. So every element is triggering "drupalOffsetChange" only when needed. Debounce guarantees 5 executions in 1s now, i'm not sure if that's the right amount.
If patch is correct then I'll move on to the remaining points.
Comment #52
jessebeach CreditAttribution: jessebeach commentedtrawekp,
box-sizing
is one of those rare CSS properties that enjoys robust support in older versions of IE. It's very handy indeed. I'd rather keep it in so that we get consistent width calculation, especially if we set min or max widths on the overlay container.http://caniuse.com/#search=box-sizing
Ok, I agree that getting rid of the resize handler in displace.js is a good thing. I went one step farther in this patch and removed the behavior altogether:
I'd rather we put the trigger of the offset change in the Drupal.displace() interface, so I changed this code back:
One of the purposes of the using Drupal.displace is to eliminate the need for modules to know how to calculate the displacement values and what event to trigger.
Your changes inspired me to change the passive displacement element discovery process into an active registration process. This way we don't need to go through the DOM each time we want to calculate the offsets on the off chance that another displacing element is present. Now, the toolbar registers the elements that have the ability to displace the overlay.
Let me know what you think of this approach. I think it solves our efficiency issues.
Comment #53
trawekp-1 CreditAttribution: trawekp-1 commentedBox-sizing does not work with IE7, so my mistake.
I'm not sure why calling a method is better than triggering an event.
Do you have in mind that fluid elements would call the method on every window resize? Debounce would be a good thing in such situation.
Comment #54
trawekp-1 CreditAttribution: trawekp-1 commentedI was thinking of this idea for a long time. I had some difficulties with managing which element displaces which edges. I think it would be better not to store theme sepratly based on edge, but in one array. It would be good idea because toolbar for example can change from top edge to left edge displacement.
Let me know what you think of this approach. I think it solves our efficiency issues.
I think that using debounce would be a good idea. It can be still added to Drupal.displace() method easily.
As I can see you've removed a behavior. I guess you hadn't have enough time and this is something I should take care of. I like the idea o registering elements much more than the previous one. I hope we're getting close. I need some rest now but I'l try to post some code tomorrow.
Comment #55
jessebeach CreditAttribution: jessebeach commentedThis patch improves the offset value calculation. Also, I removed an event trigger that passed the offset change event to the document in the iframe. It's unnecessary to propagate this event down into the iframe document.
In displace.js, we provide the offsets as a parameter when the drupalViewportOffsetChange event is triggered:
If we just trigger this event from toolbar.js, then we would have to calculate the offsets in toolbar.js and send those along when the event is triggered. By calling Drupal.displace(), we put that logic in a common location and toolbar no longer needs to worry about it.
Right now, nothing using Drupal.displace is registering elements that need to be checked on every resize event. Toolbar monitors its elements and calls Drupal.displace when they change. For the toolbar height change, it wraps its resize handler in Drupal.debounce. We might eventually need to put a resize handler in Drupal.displace later, but at the moment, we don't need it.
Comment #56
jessebeach CreditAttribution: jessebeach commentedAnd the patch.
I don't think we have a use case for this behavior yet.
Comment #57
trawekp-1 CreditAttribution: trawekp-1 commentedI haven't noticed that now toolbar registers it's elements so you're right. Behavior is not needed now.
But it was a different event and Drupal.displace was called when that event was triggered. I used event because it was easier to attach a debounce. If we're not going to use debounce now then it's better to stay with method, as it is more elegant (i guess).
I've merged all elements into one array (elements are no more grouped by edge). I think that code seems simplier now. I'm still worried that we're completely not prepared for possibility that one element overlaps another. This way offsets would accumulate, but that's wrong.
Calculating offsets should be smarter. As long as there is one element per edge it's ok but there might be more (we are allowing for this) calculations might get totally wrong. I think that the only way to resolve this problem is to calculate offsets all by ourselves. Calculations would be still simple so it's worth trying.
Modules would only register elements and call Drupal.displace, nothing more. While registering there should be an edge specified that should be considered during calculations. Also we have to add possibility to re-register element (for example when an edge should change). So no more data-offset-{edge} attribute.
Do you like the idea?
Comment #58
jessebeach CreditAttribution: jessebeach commentedCurrently, the toolbar module is wrapping the
setHeight
method in adebounce
function. The tray width (left offset) is triggered on amatchMedia
change event, not screen resize, so we don't need to debounce it. I think we're set here.From your comments in #57, I see an incompatibility in these two approaches you describe. It seems you took the first one in your patch, but are thinking about following the second.
If we eliminate the edge as a key in the object of displacing elements, then it becomes impossible to calculate those offsets in
Drupal.displace
. I agree with your second quote. Here's my logic:So, where do we go? I followed your thinking about making the offset calculation smarter. It now takes the element's offset value into account. And only the largest displacing element for an edge is used. So if multiple displacing elements exist on an edge, the smaller ones are ignored.
For instance, the overlay is being displaced here by two elements (orange and red) that a offset and bigger than the toolbar elements.
We've still got the
[data-offset-{{edge}}]
override for toolbar, too.I also wrote a test for calculateOffsets method. It's in a patch file included in this post. It applies to the 8.x-1.x branch of the testswarm project. This is not ideal. I talked to some of the Drupal JS folks and I think we'll be creating a new module that will contain all the Drupal 8 front end tests. Until then, we're stuck with this patch. Once you've got the testswarm module installed (You'll need to enable the testswarm_tests module inside of the project as well), go to this path on your installation.
?testswarm-test=displace&debug=on
The displace test should run. The file for the test is
drupal.displace.test.js
in the testswarm_tests submodule of the project.Work left to do
We need to fix the
Drupal.overlay.eventhandlerAlterDisplacedElements
method in overlay-parent.js. Somewhere in there, it's restyling the top and bottom displacing elements so that this happens:There are two elements that appear to be cut off on the right side because the overlay is setting a max-width on them that is much more narrow than the document. This code was originally there to prevent the iframe's scrollbar from going underneath the toolbar. It's not doing the right calculations any more.
The other issue is the
Drupal.displace.removeElements
method that needs work in a scenario where we're using the edge as a key to the displacing element object.Comment #59
trawekp-1 CreditAttribution: trawekp-1 commentedNot at all. "data-offset-{edge}" tells us when to calculate the edge. Also there is no problem to store the edge with an element (but your newest patch makes me totally happy of data-offset-{edge}).
The reason why I wanted this change is simple. Take a look at toolbar module for example. There is a tray, that changes from vertical to horizontal orientation. It is possible that a module would add something similar (that changes orientation) and store both top and left displacement on one element. This way one element has to be registered for top and left edge.
We have one element doubled, also we have to tell displace.js that one of displacement equals zero. We walk into the same element twice during the calculations. Code also looks more complicated.
I just don't see a reason to force grouping elements by an edge. If we would allow calculations only for one edge then it would make sense but we don't. So according to performance my idea is better. Also my idea does not exclude any of existing logic we have discussed already.
You've said that we need a possibility to remove element for a specified edge.. nothing easier. Just remove data-offset-{edge} attribute (when element displaces more than one edge) or un-register it.
Any way I have done minor changes to your patch and implemented removeElements again. Hope this is ok. This time i did not changed any logic.
Comment #60
jessebeach CreditAttribution: jessebeach commentedSo what if we say that if an element has
[data-offset-{edge}]
, use it to calculate the offset and since no value is provided, calculate it. If an element has[data-offset-{edge}="{value}"]
, use it to calculate the offset and use the value of the attribute to do it (skip calculations).This would eliminate the need for registration and turn displace into a simple API for triggering a change event and calculating offsets if necessary.
I can totally back that.
Comment #61
trawekp-1 CreditAttribution: trawekp-1 commentedI'm still using the registration. Now everything looks nice but calculateOffsets. It is written very smart but still it can scare you. This needs some clean up. I think that moving direct calculation to another function would be good. Should we do that?
I have not written behavior because I don't know how to implement element removing. While registration/unregistration works fine, removing elements registered by behavior would not be that simple (to expose as a method for modules that would be easy to understand how to use). We could be calling registerElement inside behavior, then removing elements would work the same as now and it would be simple then. I hope you have a better idea.
Is this module included in core? I can't find it there, on drupal.org too.
Comment #62
jessebeach CreditAttribution: jessebeach commentedTestswarm: http://drupal.org/project/testswarm
Front-end Tests: http://drupal.org/project/fat
I'm working on responses to your questions in #61.
Comment #63
jessebeach CreditAttribution: jessebeach commentedTestswarm: http://drupal.org/project/testswarm
Front-end Tests: http://drupal.org/project/fat
From #61
I moved this code into a function called
getRawOffset
.I really wanted to explore what this code would look like without element registration. It ends up being a lot cleaner. I also turned the
displace
method into one that returns the calculated offsets. Passingfalse
bypasses recalculation of the offsets and just returns the cached values. So under this approach, a module only ever callsDrupal.displace()
and/or registers for thedrupalViewportOffsetChange
event.Under this approach, Toolbar lets displace calculate the width of the trays, but it explicitly sets the height of the bar.
What do you think? Displace is only being called when triggered by events like the overlay loading or the orientation of the toolbar tray changing. I'm ok with the relative inefficiency of going through the DOM to find the offsetting elements if it only happens on a trigger like resizing the screen. That isn't going to happen on small devices.
Comment #64
trawekp-1 CreditAttribution: trawekp-1 commentedI think that registering elements would be better. I know that I'm boring but there might be an element calling Drupal.displace on every window resize. Fluid layouts are popular for smaller screens and smaller screens does not have powerful processors.
Please, have a look at Ubuntu Touch, I have mentioned it before but I'm not sure if you got it:
http://youtu.be/h384z7Ph0gU?t=2m14s
There are two apps running, and theirs widths can be resized. So imagine a fluid toolbar with such a device. We should be prepared for such things.
If you ask me, some kind of registering would be great. I think that behavior that searches elements with "displace" class would be just fine. A class is not a big deal. According to calling Drupal.displace() on every resize event... i guess we can hope that modules would wrap it with debounce function but we can't be sure. At least we should mention about debounce in comments of the displace method, that should be enough.
Anyway I really like the code now.
Comment #65
webchickTagging as JavaScript so it gets on peoples' radars.
Great collaboration, you two! :D Keep it up!
Comment #66
jessebeach CreditAttribution: jessebeach commentedSetting to needs review to get some other folks to chime in.
This isn't a "needs review" for RTBC. I know there are lots of comment issues and cleanup, so don't go nuts with dreditor. This is a "needs review" for the approach. We're debating two approaches.
Displacing elements are marked with a
data-offset-{edge}
attribute and auto-discovered when an offset calculation is triggeredPro
It's easy for module developers to mark an element as a displacing element.
Con
If displace is triggered often (like on window.onresize), this could be very inefficient.
Displacing elements are registered with
displace.registerElements()
Pro
The DOM is scanned for displacing elements on each trigger of displace; this is better for performance.
Con
It is slightly more complicated for module developers to mark elements as displacing.
I like the cleanliness of the code and simpler DX in option 1, but option 2 seems like it will have a better performance profile. Looking for input from others.
Comment #67
BerdirI just cancelled a ton of tests on the testbot.
If you don't want tests to be run, please use the do-not-test.patch suffix. Uploading a patch and not setting the issue to needs review means that by the time someone does that, all patches are sent to the testbot, which means you have to wait longer for the test results of the patch you actually want to be tested :)
Comment #68
nod_displace.js
I like the patch, this is making me uncomfortable but I guess it works for now. Might end up as a perf hit too:
if (!$el.is(':visible'))
Why
if (typeof displacement !== 'number' || isNaN(displacement))
and not justif (isNaN(displacement))
?Not getting
Why use the biggest number? an offset can't ever shrink ? shouldn't there be a
+=
somewhere too?calculateOffsets
Will need to be profiled and optimized once the rest is RTBC-able.overlay-parent.js
this.viewportOffsets = Drupal.displace();
There is either a naming problem or there is a method missing. It doesn't make much sense that Drupal.displace would return viewport offsets.Also if you're using .on, change all the other .bind next to it or use .bind, consistency ++
.add()
, really? one call was enough :þtableheader.js
$(window).trigger('scroll.TableHeader');
the code is already executed on a scroll event, shouldn't need to trigger another one.toolbar.js
The way
Drupal.displace();
is used ought to be an event really.Overall
So we can't have it compute the offset for left, right, top, bottom independently? seems like overkill. Tableheader would only use offset top but needs to go through the 3 others. See #1870006: HTML5 validation with table sticky header is misaligned over the toolbar for what I mean.
I'm mixed about
getRawOffset
, it means the person will add adata-offset-xxx
with rubish values in it? I ought to be the responsibility of the person adding this value to make sure it's the one that should be used. See my patch above, makes things simpler.data-offset-{edge}
More discoverable than the alternative. The pro for registerElement is really negligible here, qSA is really fast and with both methods you'll need to query the DOM for the offset value anyway so it's pretty much the same anyway, the first one has the advantage of better DX.@jesse: have a look at #1440628: [Meta] javascript toolbar/tableheader with url fragment mess too we'll have enough wierd cases to support, no need to make this one overly complex or friendly.
Sorry, not well set up for an interdiff here.
Comment #69
jessebeach CreditAttribution: jessebeach commentedThe thinking here is this. If we have two elements on the left edge that both offset the edge of the viewport, we really only want to consider the largest/most-offset one rather than add their widths.
I'm being overly cautious I guess! I'll reduce it to just isNaN and add some tests for funky values of data-offset-{edge}.
I'll just use bind for now in files that have it. I don't want to go refactoring stuff like that in this patch.
I struggle with this one. Currently, calling
Drupal.displace()
does 2 things.drupalViewportOffsetChange
event.If a module like Toolbar were to trigger
drupalViewportOffsetChange
instead of invokeDrupal.displace()
, the recalculation step would be skipped. I'm having trouble understanding how we'd get the offset information updated if modules were responsible for triggering the event and not the utitlity.Yes, a module could provide a value in
data-offset-{edge}
and that value would be used. The purpose of thegetRawOffset</code. method is to provide a common means of calculating an offset in situations where the module doesn't need to. For instance, the toolbar tray left offset is just a simple box so it adds the <code>data-offset-left
attribute and letsDrupal.displace
calculate the offset. The toolbar bar is a different story because of its CSS. It need to calulate the top offset and provide that value toDrupal.displace
.The calculation of an offset for a position fixed element when the screen is scrolled and the element is not flush against the viewport edge is not trivial. Because of this, I thought it would be useful to provide this calculation as a utility of
Drupal.displace
where we can unit test it, rather than have modules figure it out individually.Comment #70
jessebeach CreditAttribution: jessebeach commentedFinally, I've gotten through the problems with
Drupal.overlay.eventhandlerAlterDisplacedElements
. This code was complex and I believe I've cut it down significantly without losing any behavior.So, this is the patch that we start considering for commit. These are the improvements it introduces
Drupal.displace
utility. See also #787940: Generic approach for position:fixed elements like Toolbar, tableHeader, which this issue should resolve.Drupal.overlay.eventhandlerAlterDisplacedElements
now has 75% less code and is much more readable. And we rid ourselves of the last vestige of$.browser
in Drupal core module code!Changes of note in this patch
I put a window resize handler in Drupal.displace() in a behavior (and removed the resize handler from Toolbar). The reason I did this is because Toolbar and Overlay both need to respond to altered viewport displacing elements on resizes. I'd rather not have two resize handlers on window when we could just have one in a common place.
Drupal.displace()
calculates the offsets, broadcasts the updated values through thedrupalViewportOffsetChange
event and returns the offset values object to the calling function. The broadcast can be skipped by passing false to the method.Also worth noting is that these changes feed directly into #1860434: Refactor the Toolbar JavaScript to use Backbone; fix several poorly functioning behaviors. Once we get this committed, I'll work these changes into the updated Toolbar JavaScript for even more improvements and less code.
What we need now
More permutation testing
I've tried to test as many OS/browser combos as possible (in both LTR and RTL), comparing D7 to D8, to make sure that there are no regressions. In many cases, the broken D7 behavior continues without improvement in D8 because the machinations of
overlay-parent.js
and the styling of the overlay are just not going to result in a harmonious arrangement of elements in all permutations. We can't reliably know the width and placement of a scrollbar in all browsers on all platforms in all language directions. I spent more than 8 hours breaking my brain trying to prove this statement wrong and I couldn't solve it (not to say it's unsolvable). The better approach is to continue making design improvements to the Overlay presentation, specifically those being discussed in #1889150: Simplify overlay look, *only use for contextual operations* and http://groups.drupal.org/node/283223.Code review
I'd love to get this issue committed this week so we can focus on further improvements that will build on this code.
Screenshots
What's up with the borked styling on the close tab you ask? Well, this is one of those great IE issues that you dream about. Watch this video to see how IE mixes up right and left for border radius in RTL languages: http://screencast.com/t/xXrZGYPNrr. There is no way to fix this except through IE-targeted CSS which we aren't doing in D8. I hope this resolves through styling changes that a proposed for Overlay that eliminate this hanging chad element. If not, we'll have to reconsider this again before release, but my prediction is that it'll resolve itself in due time.
Comment #71
rteijeiro CreditAttribution: rteijeiro commentedHi Jesse,
I have applied the patch and now the toolbar is well placed beside the overlay but I have noticed a little horizontal scroll in the overlay (also with the toolbar collapsed). This issue appears in Chrome, Firefox and Safari with Drupal 8. Surprising it doesn't appear in Opera :)
Should I test the patch also in Drupal 7?
Comment #72
jessebeach CreditAttribution: jessebeach commentedThis patch won't apply in Drupal 7.
I think the scrollbar is caused by the hanging close tab. The redesign of the overlay should fix this.
#1953374: Implement Seven style guide for core overlay
Comment #73
rteijeiro CreditAttribution: rteijeiro commentedOk, so if the horizontal scroll would be fixed in #1953374: Implement Seven style guide for core overlay the I guess this should be considered as RTBC.
If you don't agree with me or maybe it's needed to be reviewed by someone more experienced than me, just fix the status ;)
Thanks Jesse :)
Comment #74
nod_Slow down :)
Patch incoming.
Comment #75
nod_I put offsetTop in tableheader because there was no other object with this kind of info, now that we have Drupal.displace I used that.
Exposed
Drupal.displace.offsets
andDrupal.displace.calculateOffset
so that other modules can use it. Removed some unneeded jQuery incalculateOffset
when there is a value to use.in overlay-child, removed the resize event, don't know why you added this one on top of the
drupalViewportOffsetChange
In overlay-parent same as tableheader, used
Drupal.displace.offsets
to handle the values. added a couple of ";" and a useless var in the repaint because jshint was complaining.Comment #76
nod_wow, interdiff fail. the one above is not mine.
Comment #77
jessebeach CreditAttribution: jessebeach commentedThe changes in #75 look great. Thanks for putting that together nod_! I think we're ready to get this in.
One thing you changed in the patch from #70 is the comment format for functions. Here's an example.
According to http://drupal.org/node/172169, we should be using doxygen format for type descriptors of function params and returns. The doxygen format does not include
{}
around the type descriptor. I've seen both in our codebase. Personally I'd rather go without the brackets, but I don't have a good sense of what the docs parsing requires. Do you know if the brackets are necessary? If so, we'll want to update the JS documentation docs.Comment #78
attiks CreditAttribution: attiks commentedRegarding the docs, jsdoc3 allows both "boolean" and "{boolean}", but - me personally - I would prefer "boolean", less typing and easier to read.
Comment #79
webchickCommitted and pushed to 8.x. AWESOME! :D
Probably need a change notice about this new displace library.
Comment #80
jessebeach CreditAttribution: jessebeach commentedI'll put together the change notice and post that today.
Comment #81
xjmLet's also file that followup to strip the
{}
from the docblock datatypes.Comment #82
xjmInstituting a new issue tag since I've noticed a lot of issues lately where the followups don't get filed. :)
Comment #83
star-szrWhat about the blank lines in between each @param? Should those be left for any reason?
Comment #84
klonos...coming from #787940: Generic approach for position:fixed elements like Toolbar, tableHeader
Comment #85
xjm@klonos, you don't need to post a comment to track the issue. Just click the green "Follow" button upstairs in the issue summary. :)
@Cottser, agreed, we can clean that up in a followup too.
Comment #86
star-szrShould the followup for removing
{}
and extra lines from @params ask for a patch for all core JS or just for this library?Comment #87
klonos@xjm: Yes, I know. I do this in order to remember the issue I originally came from. This helps for example when issues that were favored over their duplicates are wontfixed. This helps me get back to the issue marked as duplicate of the wontfixed issue and re-activate it ;)
Comment #88
jessebeach CreditAttribution: jessebeach commentedChange notice was posted [#1956804].
Setting to fixed.
Comment #89
star-szrTags updated, here is a follow-up for the docs touch-ups:
#1958246: Update JS documentation to follow Doxygen format (remove curly braces)
Comment #90
Wim Leers.
Comment #91
damontgomery CreditAttribution: damontgomery commentedI'm having a similar issue here. Please see my screenshots. This is on Chrome for Mac. This does NOT happen in Firefox on Mac. This is a fresh install of d8-dev from today, Jun 4, 2013. If this was the wrong issue, sorry, I couldn't find another that seemed more related.
Comment #92
jessebeach CreditAttribution: jessebeach commentedHi pandaeskimo, do you see any other JavaScript errors on the page? Could you post the top style value of the overlay from the Inspector Tools?
Comment #93
rteijeiro CreditAttribution: rteijeiro commented@pandaeskimo this issue was closed and fix commited so I guess it's better to open a new issue for that.
Comment #94
rteijeiro CreditAttribution: rteijeiro commentedTested in Chrome for Mac Version 27.0.1453.93 and can't reproduce. Something to take into consideration or maybe I should close the issue again?
Comment #95
damontgomery CreditAttribution: damontgomery commentedSorry about this, I tested it a bunch more and it's possible it was a caching issue that has been resolved locally. Reverting the issue to closed (fixed).
Comment #95.0
damontgomery CreditAttribution: damontgomery commentedAdd followup to issue summary