35

In my HTML structure, I have it set up like this:

<body>
   <main>
      <section>
      ...
      </section>

      <aside>
      ...
      </aside>
   </main>
</body>

The problem is, not all pages have <aside>

I need to select <section> and give it a max-width: 500px; ONLY when <aside> is present. The default is section { max-width: 1000px; } (when <aside> is absent)

Unlike in Selector for one tag directly followed by another tag; the user [asking the question] wants to style "B" ALL the time. Also, in this question, the user wants to select "B" (not "A")


  • I need to style <section> ONLY if <aside> is present.
  • I can't change the order of the HTML >_<
  • Can it be done with CSS only?
  • What selector do I need or how to set it up?
  • If it can't be done with CSS (I rather it be CSS-only), how can I accomplish this?
Community
  • 1
  • 1
Omar
  • 11,783
  • 21
  • 84
  • 114
  • Can it be done with CSS only? --- unfortunately not – Karl Adler Apr 15 '15 at 22:34
  • possible duplicate of [Selector for one tag directly followed by another tag](http://stackoverflow.com/questions/1132366/selector-for-one-tag-directly-followed-by-another-tag) – bpeterson76 Apr 15 '15 at 22:36
  • 1
    You are essentially asking for a previous sibling selector in css which don't exist. Possible duplicate of: http://stackoverflow.com/questions/1817792/is-there-a-previous-sibling-selector and http://stackoverflow.com/questions/7126531/css-selector-if-exist-adjacent-sibling – HJ05 Apr 15 '15 at 22:41
  • There is no need to re-edit your title to include a prefix that is already given in the tags. – BoltClock Apr 17 '15 at 05:22
  • @boltclock ditto! I'd appreciate if you don't edit the tile of my question. No need to change it to whatever you want it to be – Omar Apr 17 '15 at 23:32
  • 1
    Believe it or not, "whatever I want it to be" used to be the same as you - including the prefix in the title. But the [community has collectively agreed that that sort of thing doesn't belong](http://meta.stackexchange.com/questions/19190/should-questions-include-tags-in-their-titles) - so 1) there *is* a need to remove it 2) and it's not what *I* want it to be, it's what community policy wants. I won't edit your question again, but I can't guarantee that anybody else who comes along won't re-edit it either. – BoltClock Apr 18 '15 at 04:21

8 Answers8

50

A neat little trick

You can achieve what you want by using a trick to check if the <section> element is the only element in <main>. This will not work, if there are any other elements there. In your case it should work like this (http://jsfiddle.net/Ljm323qb/2/):

section {
     max-width: 500px;
}
/* The STAR of the show */
section:only-child {
     max-width: 1000px;
}

As illustrated in this codepen: http://codepen.io/omarjuvera/pen/ByXGyK?editors=110


General stuff on Sibling Selectors

There's the + selector which would select a sibling that comes right after the element (https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_selectors)

And there's the ~ selector which selects all following siblings (https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_selectors)

You could achieve it by putting the <aside> element before the <section> element and using a sibling selector.

Here's an example: http://jsfiddle.net/Ljm323qb/1/

A quick look in the future
Soon this will be possible, with a new :has pseudo class (http://dev.w3.org/csswg/selectors-4/#relational)
You'll be able to call something like main:has(> aside) > section { ... } but we'll have to wait for that, unfortunately :(

Omar
  • 11,783
  • 21
  • 84
  • 114
wawa
  • 4,816
  • 3
  • 29
  • 52
  • I can't change the order >_ – Omar Apr 15 '15 at 22:45
  • 1
    Hmm how about this: http://dev.w3.org/csswg/selectors-4/#only-child-pseudo will work starting at IE9... https://css-tricks.com/almanac/selectors/o/only-child/ – wawa Apr 15 '15 at 23:02
  • It relays on the fact, that the `
    ` element is the only element in the `
    `
    – wawa Apr 15 '15 at 23:07
  • @wsws it works. Edit your answer as a solution, with a jsFiddle =) – Omar Apr 15 '15 at 23:11
  • @Omar Done. I still let the general explanation for siblings there and added a method, that will work some time in the future, but is not implemented yet. – wawa Apr 15 '15 at 23:19
  • 1
    `:only-child` is not new to Selectors 4, so it's not exactly surprising that it works in IE9 - http://www.w3.org/TR/css3-selectors/#only-child-pseudo – BoltClock Apr 16 '15 at 03:40
  • Also a shorter way to write the selector using `:has()` is `section:has(+ aside)`. – BoltClock Apr 16 '15 at 03:44
  • A little bit more general hack would be :nth-last-child(1). It allows section to have previous siblings ( not limited to being the only child). But has less support – vals Apr 16 '15 at 06:55
  • @vals: Other than one or two versions of Chrome that have bugs, support for :only-child and :nth-last-child() is identical. And neither of these are hacks - unless you consider using structural pseudo-classes a hack. – BoltClock Apr 16 '15 at 07:12
  • @BoltClock I said hack meaning that we are not checking if aside is present or not, but we are checking another condition that is more or less related to it. – vals Apr 16 '15 at 08:57
4

WITHOUT JAVASCRIPT

If you can change the order of your html elements to:

<body>
   <main>
      <aside>
      ...
      </aside>

      <section>
      ...
      </section>
   </main>
</body>

You can do it like this by using a sibling selector:

aside ~ section {
    max-width: 500px;
}
deowk
  • 4,280
  • 1
  • 25
  • 34
  • Correct and very good support up to IE7. Also good approach to use ~ and not + because this way the elements don't need to be next to each others. +1 – Alessandro Incarnati Apr 15 '15 at 22:47
  • 2
    This solution would work, if he could change the DOM. Unfortunately he wrote: `I can't change the order of the HTML >_<` so I guess, that won't work. – wawa Apr 15 '15 at 23:21
  • 2
    You say "CSS ONLY", but then you go on to say "change the order of your html elements". That makes no sense. – BoltClock Apr 16 '15 at 03:42
  • I meant that this solution does not require javascript, I can understand that the heading is not 100% correct but I was speaking to the fact that the OP really did not want to use Javascript and needed a CSS only solution. – deowk Apr 16 '15 at 08:56
3

You can toggle a class .haveSidebar to the body tag using jQuery and make your CSS for the section tag depends whether this class exists on the body tag or not:

HTML

<main>
    <section>
    </section>

    <aside>
    </aside>
</main>

CSS

main {
    width:100%;
}
main aside {
    width:300px;
    background:red;
    min-height:300px;
    float:left;
}
main section {
    width:100%;
    background:green;
    min-height:300px;
    float:left;
}
body.haveSidebar main section {
    width: calc(100% - 300px);
}

JS

var sideBar = $('body > main > aside');
if (sideBar.length > 0) {
    $('body').addClass('haveSidebar');
} else {
    $('body').removeClass('haveSidebar');
}

Fiddle with aside tag

Fiddle without aside tag

Update

Solution without calc(), by using margin-left property

main aside {
    width:300px;
    background:red;
    min-height:300px;
    position:relative;
}
main section {
    width:100%;
    background:green;
    min-height:300px;
    float:left;
}
.haveSidebar main section {
    margin-left:301px;
}

Fiddle without calc()

Fares M.
  • 1,538
  • 1
  • 17
  • 18
  • Is it save to use `calc()`? I love it, but since it's only a Candidate Recommendation, I wonder if we should use it yet... (https://developer.mozilla.org/en-US/docs/Web/CSS/calc#Specifications) – wawa Apr 15 '15 at 22:49
  • There's some people using it, but there is no guarantees of compatibility and it still buggy in some browsers. – Fares M. Apr 15 '15 at 23:03
0

Even though it's not the neatest way of doing things, you can run on document ready javascript function, to check if aside tag exists, and inject an in-line css to the section tag accordingly.

Matju
  • 51
  • 6
0

It would be fairly straightforward to accomplish this with JavaScript.

For example this will work:

<script>
    window.onload = function() {
        if (document.getElementsByTagName('aside')[0]) {
           document.getElementsByTagName('section')[0].style.max-width = '500px';
        } else {
            document.getElementsByTagName('section')[0].style.max-width = '1000px';
        }
    }
</script>
Sam Redway
  • 7,605
  • 2
  • 27
  • 41
0

If you have the possibility to put your aside element before the section one, you can use the adjacent sibling selectors:

aside + section{ max-width: 500px }
Gaël Barbin
  • 3,769
  • 3
  • 25
  • 52
0

Try this, i believe it can only be done with some javascript.

if ($('main').find('aside').length>0)
{
    $('main').find('section').addClass('specificSection');
}
COLD TOLD
  • 13,513
  • 3
  • 35
  • 52
0

You can achieve it with the :has() selector, as follows:

:has(main aside) {
  section {
    max-width: 500px;
  }
}
marsidev
  • 51
  • 1
  • 3