Position an element relatively to another element from anywhere in the DOM

If you don’t care about attribution, then why does it matter if it was disregarded? The idea resurfaced and others seem on board.

Also, I didn’t disregard your idea. We have several posts back and forth where we hashed things out. Your use case was a poor example (in my opinion) and then there was your follow up comment:

It seemed to me like you were being more contrarian than actually being constructively critical. I just decided to drop it and talk it out with other posters.

Spare us the hyperbole. You seem like a smart person who has use cases for this so I don’t want you to feel like you’re not being heard. If you have great use cases that make sense to me then I’ll address them with my use cases and thoughts (as will hopefully everyone else). That’s what this thread is about. The spec writers are experts and will do their thing if they feel this thread warrants it.

Let’s get back on topic. If you want to keep arguing about this, I’d be happy to work it out over PM.

Just been reading through the majority of the comments here, sounds awesome! Just another thought, would it be beneficial to add a center option to the standard positioning - top, right, bottom, left… or would you just handle that with translate?

I think if we’re going to add center-based absolute/relative positioning to CSS, we should do it across the board, not just for element-relative positioning.

As always, though, I defer all detailed thoughts on CSS Positioning to the current editor of the CSS Positioning spec, @tabatkins.

Agreed! Sounds like a good idea.

Additional use-case: a CSS file that aims to imitate legendary typographer Edward Tufte would be able to more semantically mark up their footnotes if this existed.

Right now, it has to use <span class="sidenote"> as an inline element to get the styling it wants (positioned away from the paragraph, starting at the same height as the footnote marker). If relative-to-element positioning existed, it could use a separate <aside>.

Discussion on GitHub:

1 Like

That’s not transparent at all. The value of position should not influence the value of display. If the selector does not match, then position should simply take its default value (like other properties do when you define something that doesn’t make sense, e.g. position: banana reverts to position: static.

The behaviour you want would require some way of selecting the target of a relative-to connection from its anchor (ex. 1), or to have some fallback mechanism (ex. 2). I don’t think either of these are winners, and would probably introduce all sorts of interesting possible feedback loops.

Ex. 1

.callout {
    display: none;
    position: relative-to(.anchor);
}

.anchor:focus:relative-to-targets {
    display: block;
}

Ex. 2

.callout {
    display: none;
    position: relative-to(.anchor:focus);
}

.callout:positioned-relative-to {
    display: block;
}

Any updates to this? I would like to implement this but looks like tether is the easiest way to do so instead of messing around with debouncing resize and scroll events.

I think Tether is still the way to go. Update would be great. @tabatkins mentioned last July he had planned to pursue this in the position spec, but I have no idea what the status is on that.

I do agree with @jhnns that just accepting IDs can be problematic, however I think a stronger argument would be that this solution should support multiple elements with the same css classes to simplify the css code necessary to style the elements. After all, if I wanted to have multiple “dialog” elements that “pop up” from one central element, I think I should be able to.

is this work with multiple ids of divs or multiple div’s with same class as below

Change
sample
familiar
popover is displaying always at 1st div position...

my theme is: I need to display popover at second div element when i click on second div…as it is for third div…Can you please help me out…

Using JavaScript we can do this already, and there’s a syntax that allows you to do this sort of positioning very easily that you can use today!

With the idea of scoped styles we can write a query that targets one element and contains a block of CSS rules that can apply to any element in the DOM. If you add in the ability to evaluate a little bit of JS from within the same context as that element, suddenly you can access the scoped element’s properties, like offsetTop or offsetHeight and use the resulting values anywhere in your CSS when writing a rule for any other element!

Here’s an example where we have 2 div elements in our HTML and we want to position the #second element so it’s always touching the bottom-right corner of the #first element, regardless of their relationship in our HTML.

Consider this syntax:

<div id=second></div>
<br><br>
<p>Any HTML can separate these elements, their location & order in the DOM are irrelevant</p>
<div id=first></div>

<style>
  div {
    width: 50%;
    height: 50px;
    background: lime;
  }
  @element '#first' {
    #second {
      width: 50px;
      background: red;
      position: fixed;
      top: eval('offsetTop + offsetHeight')px;
      left: eval('offsetLeft + offsetWidth')px;
    }
  }
</style>

<script src=http://elementqueries.com/EQCSS.js></script>

Here, the eval('offsetTop + offsetHeight') is getting those values from #first because that’s the element we have scoped in our query: @element '#first' {}

It seems to me that what is desired is.

  1. A version of sticky positioning that swaps between absolute and fixed rather than relative and fixed. This part should be simple.
  2. Position relative to an element that isn’t a direct ancestor. I think the best we possibly might get is to position relative to an earlier sibling matching some additional criteria (i.e. like the general sibling selector), but that should be good enough for every use case described.
  3. Ignore certain visibility rules like z-index and overflow to make the element always visible and on top. I know overflow isn’t an issue since you can ignore that using nested absolute/fixed already. Z-index however seems problematic since that would violate core nesting behavior. It doesn’t seem like an impossible issue, but it certainly would be an ugly exception with possible performance implications since the HTML hierarchy would no longer correspond to stacking order.

Personally I solve my dialogs by setting up an event handler + mutation observer when the dialog appears, and display the dialog at the top layer as sticky. Rather than specifying an absolute position I simply give the dialog the coordinates of the element it’s supposed to be attached to as custom properties, which the event handler updates if the element moves for any reason. This preserves responsibility as much as possible since the JS only provides data, it doesn’t set styles. I also handle all movement animations with separate classes with specific prefixes, so I can temporarily copy over only those classes to the dialog and make it follow.

It’s not optimal since obviously transforms like scale should also move the dialog, but those things can’t simply be copied. Still, it works well enough that I could write a little library for it and never concern myself with it again. A CSS solution would be nice, but I can live without it.

I like this article, but I tried this and it pushes the descendant elements down when the elementally positioned element shows. The only way I know to make a tooltip-type element without shifting other elements on the page down is to use position: absolute (or fixed). An example of my password validator:

        <div class="col-4 col-12-xsmall">
            <h4>Password
                <i id="add-mb-btn-gen-pass" class="normalBtn fas fa-refresh" title="Generate Password"></i>
                <i class="normalBtn fas fa-question-circle popHelp" data-topic="password-input"></i>
            </h4>
            <input type="password" id="add-mb-pass" name="pass" value="" placeholder="Password" />
            <div id="pval-cont">
                <div id="pw-validate">
                    <h4>Password must meet the following requirements:</h4>
                    <ul>
                        <li id="pw-letter" class="pw-invalid pw-item">At least <span>one letter</span></li>
                        <li id="pw-capital" class="pw-invalid pw-item">At least <span>one capital letter</span></li>
                        <li id="pw-number" class="pw-invalid pw-item">At least <span>one number</span></li>
                        <li id="pw-symbol" class="pw-invalid pw-item">At least <span>one symbol</span></li>
                        <li id="pw-length" class="pw-invalid pw-item">Be at least <span>8 characters</span></li>
                    </ul>
                </div>
            </div>
        </div>

CSS:

#pval-cont
{
    position: relative;
}
#pw-validate {
    display: inherit;
    position: absolute;
    top: 10px;
    left: 15%;
    z-index: 20;
    width:250px;
    padding:15px;
    background:#fefefe;
    font-size:.875em;
    border-radius:5px;
    box-shadow:0 1px 3px #ccc;
    border:1px solid #ddd;
}

password-validator

This is not the complete CSS (for reasons of brevity), but if anyone wants it, I’d be happy to share. I’ve tested this out on several browsers and mobile and resized the screen and tried every mutation of the page I can think of and it stays in position no matter what I try. I’d appreciate anyone’s thoughts or ways to improve this. Thanks!

Thanks for describing your personal solution. One thing I wasn’t clear on: you describe setting up an event handler and mutation observer; however I’m curious what events you listen for. For example, do you set up scroll events, resize events? Unfortunately as far as I’m aware there is no clear complete set of events (other than perhaps scroll, resize, and all mutation events for the entire DOM tree) which will tell you when an element has changed its position or boundary. Of course you could start a requestAnimationFrame loop and check if el.getBoundingClientRect() values change each frame, but I think the hope here has been that presumably the browser can do this better.

In fact, as I describe this, I wonder if that’s really the missing API. Maybe asking to position an element relative to another is not directly what is needed, but simply an event you can listen to for changes to the values of getBoundingClientRect for an element. It may be a bit costly to do this, but I wonder if it would be possible as long as consumers don’t listen to this for more than a few elements.

Something like PositionObserver perhaps?

the problem here is, the tooltip reserve a space in the actual screen, how can I get it to go over all element

It can be achived with javascript too.

    jQuery('.scrollable_element').on('scroll', function(){
  var content_area = jQuery('#content_textarea').outerHeight(),
    offset_top = jQuery(this).scrollTop();
  if(content_area >= offset_top){
    jQuery('.sticky_element').css('top', offset_top);
  }
});

Full tutorial:

But that’s too confusing. Let’s avoid making an API that is confusing. It should just work regardless of containing block, as that would be simple and intuitive.

There’s already plenty of things in CSS that are perplexing to end users; we should focus on making things as simple as possible.

One example (of many): opacity on 3D transformed elements causes them to be flattened, which doesn’t make sense. Sure, from implementation standpoint, maybe it needed to be done. But from a user perspective, it is perplexing and undesirable. (Let’s keep 3D as a first-class citizen of the possible future of the web…)

Does that work with 3D transforms? I believe we should think in 3D by default, and have 2D as a subset, in all new APIs that we make, otherwise we may limit the ability true 3D to work well in the future (f.e. using meshes in CSS, which would be pretty cool!).

It’d be neat of sizing and position was 3D-based too; position: absolute, front: 20px or back: 30px for depth positioning within a rectangular sized space. Let’s keep stuff like that in mind! :slight_smile:

@ stuartpb, @soluml and others: While this discussion is pretty old. I hope I’m not too late for it.

Some years ago I developed a component library where you can configure all components using not only JS but also HTML and SCSS. Configuring CSS is simply done by JSON strings. To make this simple I used SCSS (to have the power of data types like map, lists, number and strings). But of course I also had the problem to somehow transform strings into elements.

My solution was to implement a new “option type” getElementsByString. This enabled element selection based on traversing methods. So you define a traverse from the originally matched element as the root.

For example:

.tooltip {
  // this matches the previous element 
  anchor: :traverse(prev());
}

A typical traverse method we use we call closestFind(.mutual-wrapper, .other-element).

Here is an example:

<div class="mutual-wrapper"> 
  <input />
  <div class="tooltip"></div>
</div>
/ * multiple of these */

Your CSS could look like this:

.tooltip {
  // this matches the element whose selector is input and has the mutual wrapper .mutual-wrapper with the root element
  anchor: :traverse(closestFind(.mutual-wrapper, input));
}

Another way to stick things together is by using unique ids or unique attribute values but moving them from CSS to HTML.

<div class="common-wrapper" id="my-id"> 
  <input />
  <div class="tooltip" data-for="my-id"></div>
</div>
/ * multiple of these */

This can also be configured like this:

.tooltip {
  // this matches the element  whose id value matches the data-for value of the "root" element
  anchor: :traverse(attrLinked(id={data-for}));
}

Of course this would actually be also a new selector feature you could write something like this:

.tooltip:attrLinked(id={data-for}) {
  // ...
}

Of course in our case the syntax is a little bit different. In our library it looks like this:

.popover {
  @include rb-js-export((
    my: "center bottom",
    at: "center top",
    anchor: "closestFind(.closest-find, .position-element)",
  ));
}

I put together a small example that you can play with:

Open the popover.scss and search for @include rb-js-export.

Offtopic: Also the true power of this lib comes with truly responsive websites and where you dynamically change, enable, disable component behavior based on the viewport. For example transform a tab control into a slider control or something else. Or if you define very “visual components” with complicated animation (for example scroll linked effects) or implement simple layout/style feature that are not (yet) available in CSS but can be made using JS.