Range slider endpoints are cut off in Firefox

2019-09-06 21:25发布

问题:

I'm working on CSS for reasonably cross-browser native range slider. In Chrome the endpoints are fine but in Firefox they are cutoff. How can I normalize them to not be cut off in Firefox?

HTML

<fieldset class="range__field">
    <input class="range is-stacking" type="range" min="0" max="10">
    <svg class="range__axis is-stacking" role="presentation" width="100%" height="10" xmlns="http://www.w3.org/2000/svg">
        <rect class="range__tick" x="0%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="10%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="20%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="30%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="40%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="50%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="60%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="70%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="80%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="90%" y="3" width="1" height="10"></rect>
        <rect class="range__tick" x="100%" y="3" width="1" height="10"></rect>
    </svg>
    <svg class="range__axis is-stacking" role="presentation" width="100%" height="14" xmlns="http://www.w3.org/2000/svg">
        <text class="range__point" x="0%" y="14" text-anchor="middle">0</text>
        <text class="range__point" x="10%" y="14" text-anchor="middle">1</text>
        <text class="range__point" x="20%" y="14" text-anchor="middle">2</text>
        <text class="range__point" x="30%" y="14" text-anchor="middle">3</text>
        <text class="range__point" x="40%" y="14" text-anchor="middle">4</text>
        <text class="range__point" x="50%" y="14" text-anchor="middle">5</text>
        <text class="range__point" x="60%" y="14" text-anchor="middle">6</text>
        <text class="range__point" x="70%" y="14" text-anchor="middle">7</text>
        <text class="range__point" x="80%" y="14" text-anchor="middle">8</text>
        <text class="range__point" x="90%" y="14" text-anchor="middle">9</text>
        <text class="range__point" x="100%" y="14" text-anchor="middle">10</text>
    </svg>
</fieldset>

SCSS

*, :before, :after {
    box-sizing: border-box;
}

@import "bourbon";

// Adapted from http://danielstern.ca/range.css
// Uses .range to target instead of [type="range"]
// Warning: Don't group vendor rules

$tick-color: silver;
$point-color: silver;
$endpoint-color: silver;
$value-color: blue;
$track-left-color: blue;
$track-right-color: silver;
$thumb-color: white;
$thumb-width: 20px;
$thumb-height: 20px;
$track-height: 5px;
$track-radius: $track-height / 2;

@function range-gradient($midpoint) {
    @return linear-gradient(to right, $track-left-color $midpoint, $track-right-color 0);
}

@mixin range-track() {
    width: 100%;
    height: $track-height;
    cursor: pointer;
    box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0px 0px 1px rgba(13, 13, 13, 0);
    background: $track-left-color;
    border-radius: $track-radius;
    border: 0;
}

@mixin range-thumb() {
    box-shadow: 1px 1px 1px black, 0px 0px 1px black;
    border: 0;
    height: $thumb-height;
    width: $thumb-width;
    border-radius: 50%;
    background: $thumb-color;
    cursor: pointer;
}

input.range {
    -webkit-appearance: none;
    position: relative;
    width: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;

    // Stick the ticks to the range
    @include transform(translateY(1em));
    margin-top: -1em;

    &:focus {
        outline: 0;
    }

    // http://stackoverflow.com/a/24203354/770127
    &::-moz-focus-outer {
        border: 0;
    }
}

input.range::-webkit-slider-thumb {
    @include range-thumb;
    -webkit-appearance: none;
    // http://bitly.com/webkit-thumb-offset
    margin-top: $track-height / 2 - $thumb-height / 2;
}

input.range::-moz-range-thumb {
    @include range-thumb;
}

input.range::-webkit-slider-runnable-track {
    @include range-track;
}

input.range::-moz-range-track {
    @include range-track;
}

input.range::-ms-track {
    width: 100%;
    height: $track-height;
    cursor: pointer;
    background: transparent;
    border-color: transparent;
    color: transparent;
}

input.range::-ms-thumb {
    @include range-thumb;
    height: $track-height;
}

input.range::-ms-fill-lower,
input.range::-ms-fill-upper {
    background: $track-left-color;
    border: 0;
    border-radius: $track-height;
    box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0px 0px 1px rgba(13, 13, 13, 0);
}

@if $track-left-color != $track-right-color {
    @for $i from 0 through 10 {
        input.range[data-range-value="#{$i}"] {
            &::-webkit-slider-runnable-track {
                @include background-image(range-gradient(percentage($i / 10)));
            }

            &::-moz-range-track {
                @include background-image(range-gradient(percentage($i / 10)));
            }
        }
    }
}

// Fit the axes to the range
// http://stackoverflow.com/a/40392411/770127
.range__axis {
    padding-left: $thumb-width / 2;
    padding-right: $thumb-width / 2;
}

.range__tick {
    fill: $tick-color;
}

.range__point {
    fill: $point-color;
}

// Endpoint color
@if $point-color != $endpoint-color {
    .range__point:first-child,
    .range__point:last-child {
        fill: $endpoint-color;
    }
}

// Current value color
@if $point-color != $value-color {
    @for $i from 0 through 10 {
        [data-range-percent="#{10 * $i}"] ~ svg .range__point[x="#{10 * $i}%"] {
            fill: $value-color;
        }
    }
}

.range__field {
    border: 0;
    padding: 0;
}

回答1:

I guess the simplest answer is to add overflow="visible" to the second <svg> element.

A better solution would be not to draw off the edge of the svg canvas in the first place though, i.e. move your text in from the edges a bit by starting at a value of x that is greater than 0% and finishing before 100%.