How to do the right positioning of css elements?
Asked Answered
N

2

6

I'm doing a slider based on noUi Slider. Was trying to achieve so to say "an elegant" solution. Since my ui handle is big I made the ui base slightly bigger with extra values in base. But those values are not allowed and handle will jump back to the allowed values. I decided to put plus and minus buttons to the place where restricted values are.

But the problem is that I can't achieve the effect that when you move the handle on to the control symbols (plus/minus) they would hide behind the handle. In my case they remain on top. And I have trouble resolving this issue.

Some help/advice would be great.

Here's my code (styles are on JSFiddle):

$(document).ready(function(){

    var sliders = document.getElementById('red'),
        input = document.getElementById('handle'),
        sliderPlus = document.getElementById('slider-amount-plus'),
        sliderMinus = document.getElementById('slider-amount-minus'),
        termCur = 500;

    noUiSlider.create(sliders, {
        start: termCur,
        step: 50,
        connect: "lower",
        range: {
            'min': 0,
            'max': 1100
        },
        pips: {
            mode: 'values',
            values: [100, 500, 1000],
            density: 4.5
        }
    });
    $('<div class="value">' + termCur + ' zł' + '</div>').appendTo($('.noUi-handle', sliders));


sliders.noUiSlider.on('change', function ( values, handle ) {
    if ( values[handle] < 100 ) {
        sliders.noUiSlider.set(100);
    } else if ( values[handle] > 1000 ) {
        sliders.noUiSlider.set(1000);
    }
});
sliders.noUiSlider.on('update', function( values ) {
    termCur = values;
    if( termCur >= 100 && termCur <= 1000 ) {
    $('.value', sliders).text(parseInt(termCur) + ' zł');}
});
sliderPlus.addEventListener('click', function(){
    if(termCur < 1000) {
        var setValue = parseInt(termCur) + 50;
        sliders.noUiSlider.set(setValue);
    }
}); 
sliderMinus.addEventListener('click', function(){
    if(termCur > 100) {
        var setValue = parseInt(termCur) - 50;
        sliders.noUiSlider.set(setValue);
    }
}); 

<div class="sliders" id="red">
        <a class="controls-symbols slider-minus" id="slider-amount-minus"><i class="fa fa-minus"></i></a>
        <a class="controls-symbols slider-plus" id="slider-amount-plus"><i class="fa fa-plus"></i></a>
    </div>

https://jsfiddle.net/o7Ly845j/

Nickelous answered 3/7, 2015 at 8:45 Comment(10)
Are you trying to achieve something like in this Fiddle?Ox
Not quite. You are hiding the minus/plus control elements when I hover. What I would like to achieve is that the control elements would be always there, like they currently are. But when I move the handle to that area (the are of control element, since it's allowed, before it bounces back) the control element would be hidden behind the handle, not remain in front.Nickelous
Are you looking for something like this Fiddle?Ox
You achieved the goal, yes. But I tried that before, it brings a problem that I couldn't solve as well. When <a> elements are inside UI-base when you click on plus or minus button you will be thrown at the very end of the slider, not +50 or -50 (in my case) how it's supposed to be. The evenListener doesn't work in this case. I tried to prevent default click action of <a> element but couldn't succeed. Maybe if I'd change <a> element to <div> or something then it would work. -Tested that out, it's not the case, it has something to do with the click event itself, you're not getting there.Nickelous
If you are going to use DOM manipulation then you may want to attach your click event with jQuerys on method like this: $(document).on('click', sliderMinus, function(e){ alert('click'); }); This will make sure the event is always attached. Before the references to the sliderMinus was cloned then removed so the event wasn't getting properlyOx
Do you have a Fiddle of that?Nickelous
Yea this Fiddle demos what I was talking about.Ox
Works great. The only problem is that .on(update) is not triggered on the last click of plus or minus (100/1000). If val>=100 && val<= 1000 is instead of val>100 && val< 1000 then on.(click) breaks. I had to put $('.value', sliders).text(termCur + ' zł'); into click events and it works now. FiddleNickelous
Question remains open... Any idea why equal signs in val>=100 && val<= 1000 break on.click event? The fix that I did isn't ideal because I need the update event to work with 100 and 1000 as well so the value would get properly updated when I slide the handle but currently it stops on 150 or 950.Nickelous
Your fiddle doesn't work for me. Just two blue boxes on top left and top right.Crosscut
R
2

So, taking the last @zgood fiddle, I guessed that the problem wasn't so much z order anymore, it was because both the slider bar and the plus/minus elements were getting the events, and by the time the plus/minus events got a hold of it, the slider was already at 1100 or 0. So I changed the events around so the plus/minus got the event first and stopped it from going to the bar. I really only had to stop mousedown and most of the problems fixed itself. After that, I could take out all the > 100 and < 1000 checks, because the bar behaved itself.

I also noticed that the slider button covered up the plus/minus once you got to 50/1050. So I changed the plus/minus to z-index:13 instead of 3 in the fiddle. I'm not sure which is better, so try 3 and 13 and make your own decision.

https://jsfiddle.net/guyschalnat/radpjf47/2/

$(document).ready(function(){

    var sliders = document.getElementById('red'),
        input = document.getElementById('handle'),
        termCur = 500;

    noUiSlider.create(sliders, {
        start: termCur,
        step: 50,
        connect: "lower",
        range: {
            'min': 0,
            'max': 1100
        },
        pips: {
            mode: 'values',
            values: [100, 500, 1000],
            density: 4.5
        }
    });
    $('<div class="value">' + termCur + ' zł' + '</div>').appendTo($('.noUi-handle', sliders));

    var c = $('#red > span').clone(true);
    $('#red > span').remove();
    $('.noUi-base').prepend(c);

    sliders.noUiSlider.on('update', function( values ) {
        var val = parseInt(values);
        termCur = val;
        $('.value', sliders).text(termCur + ' zł');
    });
    $('#slider-amount-plus').click(function(e){
        if(termCur < 1100) {
            termCur = termCur + 50;
            sliders.noUiSlider.set(termCur);
            $('.value', sliders).text(termCur + ' zł');
        }
       e.stopPropagation();
    }).mousedown(function(e){
       e.stopPropagation();
    });     
    $('#slider-amount-minus').click(function(e){ 
        if(termCur > 0) {
            termCur = termCur - 50;
            sliders.noUiSlider.set(termCur);
            $('.value', sliders).text(termCur + ' zł');
        }
    }).mousedown(function(e){
       e.stopPropagation();
    }); 

});
Reinforcement answered 14/7, 2015 at 5:26 Comment(4)
That works! Slider button covering up the plus/minus was actually the idea. noUI slider doesn't originally support disabling edges, so the handle would stop at the end of the rail, instead it goes over, so the idea was to make it wider (to make it a bit more visually appealing) and if a user slides extra left or right (0, 50, 1050, 1100) then the handle would bounce back. And while being at my self-made edge you can see the plus/minus, so that's the limit for click event as well. Added that extra stuff - fiddle. Works like a charm now.Nickelous
@llyaK Great! I wasn't sure where you were going with all this, but now I understand. I like it.Reinforcement
Minor question now. Isn't it better if I prepend the plus/minus without cloning and having extra lines of code by just inserting buttons' code into prepend? $('.noUi-base').prepend( '<a class="controls-symbols slider-minus" id="slider-amount-minus"><i class="fa fa-minus"></i></a><a class="controls-symbols slider-plus" id="slider-amount-plus"><i class="fa fa-plus"></i></a>' ); Of course I doubt that this tiny code affects the overall performance of the page, but still.Nickelous
@llyaK I think this is a judgement call. I usually strive for the most maintainable code I can write over the fastest, and that is a more intuitive thing than anything else. In some ways, I prefer to avoid the clone, maybe because I fear what happens when you update the slider base code and it turns out that they made significant changes. So go with your best guess. I didn't do much with the actual slider library, so I don't have much of a feeling one way or the other. Sorry that is such a wishy-washy answer.Reinforcement
C
2

Groups of elements with a common parent that move forward or backward together in the stacking order make up what is known as a stacking context. A full understanding of stacking contexts is key to really grasping how z-index and the stacking order work.

Every stacking context has a single HTML element as its root element. When a new stacking context is formed on an element, that stacking context confines all of its child elements to a particular place in the stacking order. That means that if an element is contained in a stacking context at the bottom of the stacking order, there is no way to get it to appear in front of another element in a different stacking context that is higher in the stacking order, even with a z-index of a billion!

New stacking contexts can be formed on an element in one of these ways:

  • When an element is the root element of a document (the element)

  • When an element has a position value other than static and a z-index value other than auto

  • When an element has an opacity value less than 1

  • In addition to opacity, several CSS properties also create stacking contexts. These include: transforms, filters, css-regions, paged media, and possibly others.


Here are the basic rules to determine stacking order within a single stacking context (from back to front):

  • The stacking context’s root element

  • Positioned elements (and their children) with negative z-index values (higher values are stacked in front of lower values; elements with the same value are stacked according to appearance in the HTML)

  • Non-positioned elements (ordered by appearance in the HTML)

  • Positioned elements (and their children) with a z-index value of auto (ordered by appearance in the HTML)

  • Positioned elements (and their children) with positive z-index values (higher values are stacked in front of lower values; elements with the same value are stacked according to appearance in the HTML)

Note: positioned elements with negative z-indexes are ordered first within a stacking context, which means they appear behind all other elements. Because of this, it becomes possible for an element to appear behind its own parent, which is normally not possible. This will only work if the element’s parent is in the same stacking context and is not the root element of that stacking context.


I created a "hackish" way to do this with jQuery, you can view the Fiddle here. This isn't a permanent solution though, I'm working on abstracting the positioning as it's using static values. I will also figure out why the CSS won't behave and post that here.

     $(document).mousemove(function (e) {
         var newpos = $(".noUi-handle").offset().left;

         if (newpos < 0) {
             $(".slider-minus").hide();
         } else { 
             $(".slider-minus").show();
         }

         if (newpos > 830) {
             $(".slider-plus").hide();  
         } else {  
             $(".slider-plus").show();
         }     
    });

update Here is a better version, now working on why the CSS won't do it for you.

Crosscut answered 9/7, 2015 at 1:38 Comment(7)
@HimeshAadeshara sorry what? I don't understand what you are saying.Crosscut
It sort of looks like the problem was answered by you. What were you looking to achieve with the bounty? I wouldn't mind digging into this, but I'd like to know what goal I am trying to achieve above and beyond what you have already done.Reinforcement
@GuySchalnat, the idea is to make it work. Notorious Pet got it to work with jQuery, but the solution is heavy and z-index solution had no success so far as well. I've looked at the competitors, none of them use similar solution, if plus/minus is used, usually they put it outside the slider which is a bummer.Nickelous
@GuySchalnat The CSS portion. The jquery solution is "hackish" at best. It should really be done in CSS and I'd like to know why the z-index stuff isn't working as I spent some time on it and couldn't figure it out.Crosscut
@IlyaK I might have another look at this if I get time this weekend. Are you still looking for a solution?Crosscut
@NotoriousPet0 yes, unfortunately I'm still fighting with this issue.Nickelous
I think I got it. See my answer.Reinforcement
R
2

So, taking the last @zgood fiddle, I guessed that the problem wasn't so much z order anymore, it was because both the slider bar and the plus/minus elements were getting the events, and by the time the plus/minus events got a hold of it, the slider was already at 1100 or 0. So I changed the events around so the plus/minus got the event first and stopped it from going to the bar. I really only had to stop mousedown and most of the problems fixed itself. After that, I could take out all the > 100 and < 1000 checks, because the bar behaved itself.

I also noticed that the slider button covered up the plus/minus once you got to 50/1050. So I changed the plus/minus to z-index:13 instead of 3 in the fiddle. I'm not sure which is better, so try 3 and 13 and make your own decision.

https://jsfiddle.net/guyschalnat/radpjf47/2/

$(document).ready(function(){

    var sliders = document.getElementById('red'),
        input = document.getElementById('handle'),
        termCur = 500;

    noUiSlider.create(sliders, {
        start: termCur,
        step: 50,
        connect: "lower",
        range: {
            'min': 0,
            'max': 1100
        },
        pips: {
            mode: 'values',
            values: [100, 500, 1000],
            density: 4.5
        }
    });
    $('<div class="value">' + termCur + ' zł' + '</div>').appendTo($('.noUi-handle', sliders));

    var c = $('#red > span').clone(true);
    $('#red > span').remove();
    $('.noUi-base').prepend(c);

    sliders.noUiSlider.on('update', function( values ) {
        var val = parseInt(values);
        termCur = val;
        $('.value', sliders).text(termCur + ' zł');
    });
    $('#slider-amount-plus').click(function(e){
        if(termCur < 1100) {
            termCur = termCur + 50;
            sliders.noUiSlider.set(termCur);
            $('.value', sliders).text(termCur + ' zł');
        }
       e.stopPropagation();
    }).mousedown(function(e){
       e.stopPropagation();
    });     
    $('#slider-amount-minus').click(function(e){ 
        if(termCur > 0) {
            termCur = termCur - 50;
            sliders.noUiSlider.set(termCur);
            $('.value', sliders).text(termCur + ' zł');
        }
    }).mousedown(function(e){
       e.stopPropagation();
    }); 

});
Reinforcement answered 14/7, 2015 at 5:26 Comment(4)
That works! Slider button covering up the plus/minus was actually the idea. noUI slider doesn't originally support disabling edges, so the handle would stop at the end of the rail, instead it goes over, so the idea was to make it wider (to make it a bit more visually appealing) and if a user slides extra left or right (0, 50, 1050, 1100) then the handle would bounce back. And while being at my self-made edge you can see the plus/minus, so that's the limit for click event as well. Added that extra stuff - fiddle. Works like a charm now.Nickelous
@llyaK Great! I wasn't sure where you were going with all this, but now I understand. I like it.Reinforcement
Minor question now. Isn't it better if I prepend the plus/minus without cloning and having extra lines of code by just inserting buttons' code into prepend? $('.noUi-base').prepend( '<a class="controls-symbols slider-minus" id="slider-amount-minus"><i class="fa fa-minus"></i></a><a class="controls-symbols slider-plus" id="slider-amount-plus"><i class="fa fa-plus"></i></a>' ); Of course I doubt that this tiny code affects the overall performance of the page, but still.Nickelous
@llyaK I think this is a judgement call. I usually strive for the most maintainable code I can write over the fastest, and that is a more intuitive thing than anything else. In some ways, I prefer to avoid the clone, maybe because I fear what happens when you update the slider base code and it turns out that they made significant changes. So go with your best guess. I didn't do much with the actual slider library, so I don't have much of a feeling one way or the other. Sorry that is such a wishy-washy answer.Reinforcement

© 2022 - 2024 — McMap. All rights reserved.