Html input range вертикальный

Creating vertical range controls

By default, if a browser renders a range input as a slider, it will render it so that the knob slides left and right. When supported, we will be able to make the range vertical, to slide up and down with CSS by declaring a height value greater than the width value. This is not actually implemented yet by any of the major browsers. (See Firefox bug 981916, Chrome bug 341071). It also, perhaps, may still be under discussion.

In the meantime, we can make the range vertical by rotating it using CSS transforms, or, by targeting each browser engine with their own method, which includes setting the appearance to slider-vertical , by using a non-standard orient attribute in Firefox, or by changing the text direction for Internet Explorer and Edge.

Horizontal range control

Consider this range control:

input type="range" id="volume" min="0" max="11" value="7" step="1">

This control is horizontal (at least on most if not all major browsers; others might vary).

Standards-based vertical range control

According to the specification, making it vertical requires adding CSS to change the dimensions of the control so that it’s taller than it is wide, like this:

#volume < height: 150px; width: 50px; >
input type="range" id="volume" min="0" max="11" value="7" step="1">

You can create a vertical range control by drawing a horizontal range control on its side. The easiest way is to use CSS: by applying a transform to rotate the element, you can make it vertical. Let’s take a look.

"slider-wrapper"> input type="range" min="0" max="11" value="7" step="1">

Now we need some CSS. First is the CSS for the wrapper itself; this specifies the display mode and size we want so that the page lays out correctly; in essence, it’s reserving an area of the page for the slider so that the rotated slider fits into the reserved space without making a mess of things.

.slider-wrapper < display: inline-block; width: 20px; height: 150px; padding: 0; >

Then comes the style information for the element within the reserved space:

.slider-wrapper input < width: 150px; height: 20px; margin: 0; transform-origin: 75px 75px; transform: rotate(-90deg); >

The size of the control is set to be 150 pixels long by 20 pixels tall. The margins are set to 0 and the transform-origin is shifted to the middle of the space the slider rotates through; since the slider is configured to be 150 pixels wide, it rotates through a box which is 150 pixels on each side. Offsetting the origin by 75px on each axis means we will rotate around the center of that space. Finally, we rotate counter-clockwise by 90°. The result: a range input which is rotated so the maximum value is at the top and the minimum value is at the bottom.

The appearance property has a non-standard value of slider-vertical that, well, makes sliders vertical.

We use the same HTML as in the previous examples:

input type="range" min="0" max="11" value="7" step="1">

We target just the inputs with a type of range:

Читайте также:  PHP File create/write Example

Using the orient attribute

In Firefox only, there is a non-standard orient property.

Use similar HTML as in the previous examples, we add the attribute with a value of vertical :

input type="range" min="0" max="11" value="7" step="1" orient="vertical">

writing-mode: bt-lr;

The writing-mode property should generally not be used to alter text direction for internationalization or localization purposes, but can be used for special effects.

We use the same HTML as in the previous examples:

input type="range" min="0" max="11" value="7" step="1">

We target just the inputs with a type of range, changing the writing mode from the default to bt-lr , or bottom-to-top and left-to-right:

input[type="range"] < writing-mode: bt-lr; >

Putting it all together

As each of the above examples works in different browsers, you can put all of them in a single example to make it work cross browser:

We keep the orient attribute with a value of vertical for Firefox:

input type="range" min="0" max="11" value="7" step="1" orient="vertical">

We target just the inputs with a type of range, changing the writing mode from the default to bt-lr , or bottom-to-top and left-to-right, for Edge and Internet Explorer, and add -webkit-appearance: slider-vertical for all -webkit-based browsers:

input[type="range"] < writing-mode: bt-lr; -webkit-appearance: slider-vertical; >

Specifications

Источник

Cross-browser Vertical Slider using input type=»range»

Vertical sliders have been around in the form of since IE 10, but vertical sliders remain challenging. This post describes a cross-browser technique for styling vertical sliders. It then dives into building it out as a fully customizable web component.

Browser Support

  • Chrome 94 (Sep 21, 2021) (implemented via -webkit-appearance: slider-vertical )
  • Safari 15 (Sep 19, 2021) (implemented via -webkit-appearance: slider-vertical )
  • IE 10 (2012!) – all versions of Edge via writing-mode: bt-lr
  • Firefox (date unknown) (implemented with an attribute:

Total support at time of writing based on Can I use is 48%.

That’s just not going to cut it and, on top of that, its largely supported through vendor prefixes. Perusing the archives, I stumbled across this article that references orientation, but its from 2017 and doesn’t account for these new features. However, it does make a great point: you cant remove default browser styling. To do that, you have to set -webkit-appearance: none on the range.

What does the spec say?

The spec says if the height is greater than the width, make it vertical. Sadly, due to the way browsers have implemented ranges in practice, its not an easy thing to do.

Let’s see if we can solve this problem ourselves!

First steps

Let see how the browsers render vertical sliders natively:

There’s some layout stuff happening. Notably, the default display is inline-block which is important to remember for later. We don’t want our vertical slider to unnaturally break up the document flow — we should be able to place things above, below, before, and after the slider.

Recreating the Slider

This gives us an idea of how the vertical slider is intended function. Now we can reimplement it as a custom element. Internally, we’ll abstract the vendor-specific details into a single, clean API that Just Works™️ in all browsers.

Since raw custom elements aren’t much fun to write, let’s use FAST Element for this exercise. To get up and running quickly, I’ll also use my FAST Element TypeScript starter.

Читайте также:  Найти площадь треугольника си шарп

We’re going to need two files, one for logic and one for styles. Let’s call them slider.ts and slider.styles.ts .

// slider.ts import  FASTElement, html, attr, customElement > from '@microsoft/fast-element' import type  ViewTemplate > from '@microsoft/fast-element' import  styles > from './slider.styles.ts' // This is the component's internal template that gets rendered in the shadow root const template: ViewTemplate = htmlVerticalSlider>` 
$x => x.min>" max p">$x => x.max>" step p">$x => x.step>" @input p">$<(x,c) => x.handleInput(c.event)>" >
`
// Defines a custom element called . This is the "host element." @customElement( name: 'vertical-slider', template, styles >) export class VerticalSlider extends FASTElement // Defines attributes that FAST will map to properties, e.g. @attr min = 0 @attr value = 50 @attr max = 100 @attr step = 1 // Called when the internal slider emits the input event. We use this to keep the host element's value in sync. handleInput (event: Event): void const input = event.currentTarget as HTMLInputElement this.value = Number(input.value) > > // Add so TypeScript "sees" it declare global interface HTMLElementTagNameMap 'vertical-slider': VerticalSlider > >

Since we’re using under the hood, the component accepts the same attributes and passes them through to it. The handleInput() function is in charge of updating the value of the web component when the slider moves.

Basic Styling

Now that we have the logic in place, let’s give the slider some styles.

// slider.styles.ts // Normalize is a variable that comes from the fast-element-typescript-starter. import  normalize > from '../normalize' import  css > from '@microsoft/fast-element' export const styles = css` $normalize> :host < display: inline-block; position: relative; --width: 16px; --height: 175px; /* Native vertical sliders have increased rightward margin. */ margin-right: calc(var(--width) / 2); >.wrapper < width: var(--width); height: var(--height); position: relative; /* center the slider */ margin: 0 auto; >.slider < /* width and height get swapped due to how the transform gets calculated, they will get reversed when turned vertical */ width: var(--height); height: var(--width); left: 0; /* Pushes the slider slightly upward off the bottom of the line */ bottom: -0.75em; /* Rotation -90deg makes sliding upward increase, and downward decrease. TranslateY centers us since we're absolutely positioned */ transform: rotate(-90deg) translateY(calc(var(--width) / 2)); transform-origin: left; position: absolute; >` 

Let’s see how our custom element compares to the native slider we started out with.

Comparison of web component to native slider

Here’s what it looks like in various browsers.

Comparison of all browsers with web component

(From left to right: Firefox, Chrome, Safari, Edge)

Advanced Styling

This is a good start, but there are two problems we’re facing now.

  1. Our slider looks different in all 4 browsers (Chrome, FF, Edge, and Safari)
  2. The slider’s styles aren’t easy to customize

Let’s solve both of these problems by disecting the slider into two parts: the thumb and the track. The thumb is the indicator you grab and move around. The track the long axis that the thumb moves along. We want to make both of these easy to customize.

Styling the thumb

It’s important to note that we have to seperate vendor-specific selectors to accommodate polyfills. We can’t do:

.slider::ms-thumb, .slider::-moz-range-thumb  /* nope! */ > 

To keep things dry, we’ll bake our thumb styles into a variable and reuse them in each vendor-specific selector.

// slider.styles.ts const thumbStyles = ` cursor: pointer; height: var(--thumb-size); width: var(--thumb-size); border-radius: 50%; border: none; outline: 1px solid var(--thumb-outline); margin-top: calc((var(--thumb-size) / -2 + var(--track-width) / 2) - 1px); background-color: var(--thumb-color); ` export const styles = ` :host < --thumb-border: rgb(80, 80, 80); --thumb-color: rgb(80, 80, 80); --thumb-size: 16px; /* Allows us to style the slider our own way */ -webkit-appearance: none; >/* https://css-tricks.com/styling-cross-browser-compatible-range-inputs-css/ */ /* Special styling for WebKit/Blink */ .slider::-webkit-slider-thumb < -webkit-appearance: none; $thumbStyles> > /* All the same stuff for Firefox */ .slider::-moz-range-thumb  $thumbStyles> > /* All the same stuff for IE */ .slider::-ms-thumb  $thumbStyles> > ` 

Comparison of thumb across browsers

The thumb now looks like this. Now we’ll focus on the track.

Styling the Track

We’ll split the track up since each browser implements them a bit differently.

// slider.styles.ts const trackStyles = ` width: var(--track-height); height: var(--track-width); background-color: var(--track-color); border: 1px solid var(--track-border-color); border-radius: var(--track-radius); ` export const styles = ` :host < --track-height: 100%; --track-width: 6px; --track-radius: 6px; --track-color: rgb(225, 225, 225); --track-border-color: rgb(105, 105, 105); >.slider::-webkit-runnable-track  $trackStyles> > .slider::-moz-range-track  $trackStyles> > ` 

Now we have a styled thumb and a track!

Bonus Feature

Since we have more control over how the slider is being styled, we can take things a step further and make the track reflect the current value by styling it differently above and below the thumb. Unfortunately, most browsers don’t make this easy for us, but Firefox exposes a progress pseudo selector. Let’s start with that.

export const styles = ` :host < --progress-color: rgb(20, 122, 255); --progress-border-color: rgb(20, 122, 255); /* . */ >/* . */ .slider::-moz-range-progress < width: var(--track-height); height: var(--track-width); background-color: var(--progress-color); border: 1px solid var(--progress-border-color); border-radius: var(--track-radius); >` 

This covers us for Firefox, but we need to target the remaining browsers. Fortunately, we can use a linear gradient and make use of CSS custom properties to tap into the slider’s value.

.volume-slider::-webkit-slider-runnable-track  background: linear-gradient(to right, var(--progress-color) 0%, var(--progress-color) var(--progress-percent), var(--track-color) var(--progress-percent), var(--track-color) 100%); > 

Astute readers may have noticed that we haven’t defined —progress-percent yet. This value needs to reflect the thumb’s position along the track as a percentage. Let’s go back to the component and make it set this variable when the value changes.

Go to src/vertical-slider/index.ts . Here we will add the logic that will update our progress-percent as the value of the slider changes.

// slider.ts export class VerticalSlider extends FASTElement  // . connectedCallback (): void  super.connectedCallback() const progressPercent = `$<(this.value / this.max) * 100>` this.updateProgress(progressPercent) > valueChanged (_oldValue: number, newValue: number)  const progressPercent = `$<(newValue / this.max) * 100>` this.updateProgress(progressPercent) > updateProgress(value: string)  this.style.setProperty('--progress-percent', value + '%') > > 

At last, the fruit of our labor! A cross-browser, consistent vertical slider custom element that uses the browser’s built-in !

A cross-browser comparison of the slider

And heres a codepen of everything in 1 file.

Closing Thoughts

There are even more features we could bake into the component such as a disabled state, tooltip values, and a wide array of other things you can find present in something like Shoelace’s Range Component. However, those are outside the scope of this post. I’ll consider them an exercise for the reader. In addition, if you need a fully customizable slider, you may be better off constructing your own not using an purely because it is fairly inflexible. It doesn’t accept nested children. Styling is difficult due to browser prefixes, and using custom thumbs is also challenging.

If you’d like to see the component’s full source, check out the repo on GitHub. This component is not currently released as an NPM package.

Источник

Оцените статью