1

I have the following HTML structure.

<div class="fieldset">
    <p>Normal Input<i class="icon-question tooltip-top" title="Text Goes Here"></i></p>
    <div>
        <span><i class="icon-cart"></i></span>
        <input name="">
        <span><i class="icon-cart"></i></span>
        <i class="icon-question tooltip-top" title="Text Goes Here"></i>
    </div>
</div>

I am trying to change the settings of the border-radius according to where the <input> is positioned.

For example, if the <input> has a <span> just before it, its border-radius will be zero from top and bottom left.

I did this by using:

.fieldset > div > span + input {
    border-radius: 0 4px 4px 0;
}

But, when there is a <span> after the <input> the input border radius should be zero from only the right top and bottom side. Obviously I can't use the + selector for this one.

How can I achieve the desired results for when the <span> is after the <input> without using JavaScript and altering the HTML?

UPDATE:

Following the answers below and especially BoltClock♦ methods - it seems that the problem is nearly solved! The image below demonstrate all the different scenarios and the code used to apply them.

enter image description here The only scenario that is not yet working, is when there is only one span before the input.

The current CSS is:

/*Fieldsets*/
.fieldset {
    width: 100%;
    display: table;
  position: relative;
  white-space: nowrap;
  margin-bottom: 15px;
}

.fieldset:last-of-type {
    margin-bottom: 0;
}

        /*Fieldsets > Labels*/
        .fieldset > p { 
            width: 1%;
            margin-bottom: 3px;
        }  

        /*Fieldsets > Input Container*/
        .fieldset > div {
            display: table-row;
            position: relative;
        }  

            .fieldset > div > * {
                display: table-cell;
              white-space: nowrap;
              vertical-align: middle;
                position: relative;
            }  

            /*Fieldsets > Input + Icon*/
            .fieldset > div > span {
                border: 1px solid #B0C2CE;
                padding: 5px 15px;
                font-weight: bold;
                width: 1%;
            }  

            /*Fieldsets > Input + Icon Senarios*/
            .fieldset > div > span:first-of-type {
                border-right: 0;
                border-radius: 4px 0 0 4px;
            } 

            .fieldset > div > span:last-of-type {
                border-left: 0;
                border-radius: 0 4px 4px 0;
            }  

            .fieldset > div > span:not(:only-of-type) + input {
                border-radius: 0;
            }

            .fieldset > div > span + input,
            .fieldset > div > span + textarea,
            .fieldset > div > span + select,
            .fieldset > div > span + .select-dropdown-single .select-dropdown-input,
            .fieldset > div > span + .select-dropdown-multi .select-input {
                border-radius: 0 4px 4px 0;
            }

            /*Fieldsets > Input + Help toolTip icon*/
            .fieldset > div [class^="tooltip-"],
            .fieldset > div [class*=" tooltip-"] {
                text-align: center;
                width: 30px;
            } 

The HTML:

<div class="fieldset">
                <p>Normal Input</i></p>
                <div>
                    <input name="">
                </div>
            </div>

            <div class="fieldset">
                <p>Normal Input</p>
                <div>
                    <span><i class="icon-cart"></i></span>
                    <input name="">
                </div>
            </div>

            <div class="fieldset">
                <p>Normal Input</p>
                <div>
                    <span><i class="icon-cart"></i></span>
                    <input name="">
                    <span><i class="icon-cart"></i></span>
                </div>
            </div>

            <div class="fieldset">
                <p>Normal Input</p>
                <div>
                    <span><i class="icon-cart"></i></span>
                    <input name="">
                    <span><i class="icon-cart"></i></span>
                    <i class="icon-question tooltip-top" title="Text Goes Here"></i>
                </div>
            </div>
Leo
  • 967
  • 2
  • 14
  • 37

3 Answers3

3

Normally, you cannot target an element that is a preceding sibling of another element regardless of HTML structure, because there is no previous sibling selector.

However, given your structure, there are a number of possible cases that can still be accounted for using what is currently available to you. If there will only be at most one <span> on either side of the <input>, or both sides, and no other <span> elements within this <div> element, you will be able to target the <input> in all of these cases.

As given in the question, when there is a <span> element appearing only before the <input>, use an adjacent sibling selector to target the <input>:

<div class="fieldset">
    <p>Normal Input<i class="icon-question tooltip-top" title="Text Goes Here"></i></p>
    <div>
        <span><i class="icon-cart"></i></span>
        <input name="">
        <i class="icon-question tooltip-top" title="Text Goes Here"></i>
    </div>
</div>
.fieldset > div > span + input {
    border-radius: 0 4px 4px 0;
}

When there is a <span> element appearing only after the <input>, then if that causes <input> to be the first child of its parent <div>, you can use :first-child to target that <input> only in such a situation:

<div class="fieldset">
    <p>Normal Input<i class="icon-question tooltip-top" title="Text Goes Here"></i></p>
    <div>
        <input name="">
        <span><i class="icon-cart"></i></span>
        <i class="icon-question tooltip-top" title="Text Goes Here"></i>
    </div>
</div>
.fieldset > div > input:first-child {
    border-radius: 4px 0 0 4px;
}

When <span> elements appear both before and after the <input>, you can use :not(:only-of-type) on the first <span> with an adjacent sibling selector to prevent matching that first <span> in such a situation:

<div class="fieldset">
    <p>Normal Input<i class="icon-question tooltip-top" title="Text Goes Here"></i></p>
    <div>
        <span><i class="icon-cart"></i></span>
        <input name="">
        <span><i class="icon-cart"></i></span>
        <i class="icon-question tooltip-top" title="Text Goes Here"></i>
    </div>
</div>
.fieldset > div > span:not(:only-of-type) + input {
    border-radius: 0;
}

As a bonus, since you're dealing with the border-radius property, keep in mind that you can use its longhands selectively to avoid having to deal with the third case (<span> on both sides) altogether:

.fieldset > div > input {
    border-radius: 4px;
}

.fieldset > div > span:only-of-type + input {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.fieldset > div > input:first-child {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

Notice the use of :only-of-type in this case however, in order to prevent that selector from matching if <span> elements are present on both sides.

Community
  • 1
  • 1
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • Thank you, I have used the answer of the Both Before & After and it worked. However, your answer to the Only After did not, the reason being is there might be no spans at all and only input in the div. Therefore the input will have a zero radius from right top and bottom regardless. It should only have zero radius when there is a span. – Leo Apr 01 '15 at 13:20
  • 1
    @Leo: Good point. Having no spans complicates things a little, but it should still be doable. Is that i element the only other possible sibling? If so, you can target the input when there are no spans using `input:first-child:nth-last-child(2)` - this assumes the last child is that i element, and effectively means "Select an input that is the first of exactly two children in its parent." – BoltClock Apr 01 '15 at 13:23
  • Thank you ever so much! Please read my update in my question above. – Leo Apr 01 '15 at 13:42
1

I can simply give the main container a class accordingly, but I want to see if there is a way to do it without the need to alter the HTML.

No, there is no selectors which select previous sibling (Is there a previous sibling selector?)

There is only a draft in the next CSS version: http://dev.w3.org/csswg/selectors-4/#general-sibling-combinators

phts
  • 3,889
  • 1
  • 19
  • 31
0

In your special case (if your HTML does not alter alot from your given example) you could use something like this:

try removing / commenting out the first span and see the difference. It would simply apply border-radius: 0 to all input elements except for those where the previous element is a span

//EDIT

you can now see the difference directly, no commenting out necessary

.fieldset > div > input {
    border-radius: 0;
}

.fieldset > div > span + input {
    border-radius: 0 4px 4px 0 !important;
}

JSFiddle

Doml The-Bread
  • 1,761
  • 1
  • 11
  • 17