首页 > 解决方案 > 自定义范围输入在 Blazor 上传递到默认值

问题描述

我试图用 2 个处理程序进行范围输入,我找到了一个 javascript 和 css 文件,问题是当我运行项目时,自定义范围输入出现并且 1 秒后它传递到默认值。通过运行项目并注意范围输入并刷新页面,您可以在这个Github 存储库中看到这个问题。

应该显示这个范围输入:

范围输入

但是,正如我所说,它会在您运行项目时的前几毫秒内显示,然后它会传递到默认值。

HTML 代码:

<input type="range" multiple value="0,100" />

CSS 代码:

@supports (--css: variables) {
    input[type="range"].multirange {
        padding: 0;
        margin: 0;
        display: inline-block;
        vertical-align: top;
    }

        input[type="range"].multirange.original {
            position: absolute;
        }

            input[type="range"].multirange.original::-webkit-slider-thumb {
                position: relative;
                z-index: 2;
            }

            input[type="range"].multirange.original::-moz-range-thumb {
                transform: scale(1); /* FF doesn't apply position it seems */
                z-index: 1;
            }

        input[type="range"].multirange::-moz-range-track {
            border-color: transparent; /* needed to switch FF to "styleable" control */
        }

        input[type="range"].multirange.ghost {
            position: relative;
            background: var(--track-background);
            --track-background: linear-gradient(to right, transparent var(--low), var(--range-color) 0, var(--range-color) var(--high), transparent 0 ) no-repeat 0 45% / 100% 40%;
            --range-color: hsl(190, 80%, 40%);
        }

            input[type="range"].multirange.ghost::-webkit-slider-runnable-track {
                background: var(--track-background);
            }

            input[type="range"].multirange.ghost::-moz-range-track {
                background: var(--track-background);
            }
}

Javascript代码:

(function () {
    "use strict";

    var supportsMultiple = self.HTMLInputElement && "valueLow" in HTMLInputElement.prototype;

    var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");

    var multirange = function (input) {
        if (supportsMultiple || input.classList.contains("multirange")) {
            return;
        }

        var value = input.getAttribute("value");
        var values = value === null ? [] : value.split(",");
        var min = +(input.min || 0);
        var max = +(input.max || 100);
        var ghost = input.cloneNode();

        input.classList.add("multirange", "original");
        ghost.classList.add("multirange", "ghost");

        input.value = values[0] || min + (max - min) / 2;
        ghost.value = values[1] || min + (max - min) / 2;

        input.parentNode.insertBefore(ghost, input.nextSibling);

        Object.defineProperty(input, "originalValue", descriptor.get ? descriptor : {
            // Fuck you Safari >:(
            get: function () { return this.value; },
            set: function (v) { this.value = v; }
        });

        Object.defineProperties(input, {
            valueLow: {
                get: function () { return Math.min(this.originalValue, ghost.value); },
                set: function (v) { this.originalValue = v; },
                enumerable: true
            },
            valueHigh: {
                get: function () { return Math.max(this.originalValue, ghost.value); },
                set: function (v) { ghost.value = v; },
                enumerable: true
            }
        });

        if (descriptor.get) {
            // Again, fuck you Safari
            Object.defineProperty(input, "value", {
                get: function () { return this.valueLow + "," + this.valueHigh; },
                set: function (v) {
                    var values = v.split(",");
                    this.valueLow = values[0];
                    this.valueHigh = values[1];
                    update();
                },
                enumerable: true
            });
        }

        if (typeof input.oninput === "function") {
            ghost.oninput = input.oninput.bind(input);
        }

        function update() {
            ghost.style.setProperty("--low", 100 * ((input.valueLow - min) / (max - min)) + 1 + "%");
            ghost.style.setProperty("--high", 100 * ((input.valueHigh - min) / (max - min)) - 1 + "%");
        }

        ghost.addEventListener("mousedown", function passClick(evt) {
            // Are the ghost and input elements inverted? (ghost is lower range)
            var isInverted = input.valueLow == ghost.value;
            // Find the horizontal position that was clicked (as a percentage of the element's width)
            var clickPoint = evt.offsetX / this.offsetWidth;
            // Map the percentage to a value in the range (note, assumes a min value of 0)
            var clickValue = max * clickPoint;

            // Get the distance to both high and low values in the range
            var highDiff = input.valueHigh - clickValue;
            var lowDiff = Math.abs(input.valueLow - clickValue);

            if (isInverted ? highDiff < lowDiff : lowDiff < highDiff) {
                // The low value is closer to the click point than the high value
                // We should update the low value input
                var passEvent = new MouseEvent("mousedown", evt);
                // Pass a new event to the low "input" element (which is obscured by the
                // higher "ghost" element, and doesn't get mouse events outside the drag handle
                input.dispatchEvent(passEvent);
                // The higher "ghost" element should not respond to this event
                evt.preventDefault();
                return false;
            }
        });
        input.addEventListener("input", update);
        ghost.addEventListener("input", update);

        update();
    }

    multirange.init = function () {
        [].slice.call(document.querySelectorAll("input[type=range][multiple]:not(.multirange)")).forEach(multirange);
    }

    if (typeof module === "undefined") {
        self.multirange = multirange;
        if (document.readyState == "loading") {
            document.addEventListener("DOMContentLoaded", multirange.init);
        }
        else {
            multirange.init();
        }
    } else {
        module.exports = multirange;
    }

})();

感谢您的关注。

标签: blazor

解决方案


我采用了提供的 CSS 并将其应用于没有 JavaScript 的 Blazor。

下面是一个概念证明(需要在 CSS 上做更多工作——尤其是围绕焦点轮廓),您可以将其作为基础。

它与提供的 JS 版本略有不同,因为我不喜欢 MouseDown 处理 - 感觉很hacky。

像这样使用这个示例控件:

<RangeControl @bind-ValueHigh="ValueHigh" @bind-ValueLow="ValueLow"></RangeControl>
<span>@ValueLow</span>-<span>@ValueHigh</span>

@code {
    int ValueHigh=75;
    int ValueLow=25;
}

创建一个 RangeControl.razor 文件并将其放入其中注意:标记/CSS/代码都在一个文件中,以便于在此处发布,在现实生活中您可能希望将其中的一些分开(或者您可能不 - 使用 Blazor,这是您的选择)

<div id="rc-@ID">
@(new MarkupString($@"<style>
        #rc-{ID} {{
            position: relative;
            width: {Width}px;
        }}

        #rc-{ID} > input[type='range'] {{            
            padding: 0;
            margin: 0;
            display: inline-block;
            vertical-align: top;
            width: 100%;
            --range-color: hsl(190, 80%, 40%);
            background: var(--track-background);
        }}
        #rc-{ID} > input[type='range']::-moz-range-track {{
            border-color: transparent; /* needed to switch FF to 'styleable' control */
        }}
        #rc-{ID} > input[name='low-range'] {{
            position: absolute;
            --track-background: linear-gradient(to right, transparent {100 * (ValueLow - MinValue) / (MaxValue - MinValue) + 1}%, var(--range-color) 0, var(--range-color) {100 * (ValueHigh - MinValue) / (MaxValue - MinValue) - 1}%, transparent 0 ) no-repeat 0 45% / 100% 40%;
        }}
        #rc-{ID} > input[name='low-range']::-webkit-slider-thumb {{
            position: relative;
            z-index: 2;
        }}
        #rc-{ID} > input[name='low-range']::-moz-range-thumb {{
                transform: scale(1); /* FF doesn't apply position it seems */
                z-index: 1;
            }}
        #rc-{ID} > input[name='high-range'] {{
            position: relative;
            --track-background: linear-gradient(to right, transparent {100 * (ValueLow - MinValue) / (MaxValue - MinValue) + 1}%, var(--range-color) 0, var(--range-color) {100 * (ValueHigh - MinValue) / (MaxValue - MinValue) - 1}%, transparent 0 ) no-repeat 0 45% / 100% 40%;
            clip-path: polygon({50 * (ValueLow + ValueHigh) / (MaxValue - MinValue) + 1}% 0, 100% 0, 100% 100%, {50 * (ValueLow + ValueHigh) / (MaxValue - MinValue) + 1}% 100%);
        }}

        #rc-{ID} > input[type='range']::-webkit-slider-runnable-track {{
            background: var(--track-background);
        }}

        #rc-{ID} > input[type='range']::-moz-range-track {{
            background: var(--track-background);
        }}
    </style>"))
    <input name="low-range" type="range" min="@MinValue" max="@MaxValue" @bind="@ValueLow" @bind:event="oninput" />
    <input name="high-range" type="range" min="@MinValue" max="@MaxValue" @bind="@ValueHigh" @bind:event="oninput" />
</div>
@code
{
    int _valueLow = 0;
    int _valueHigh = 100;
    [Parameter] public int Width { get; set; } = 200;
    [Parameter] public int MinValue { get; set; } = 0;
    [Parameter] public int MaxValue { get; set; } = 100;
    [Parameter] public int ValueLow {
        get => Math.Min(_valueLow,_valueHigh);
        set {
            if (_valueLow.Equals(value))
                return;
            _valueLow = value;
            if (_valueLow >= _valueHigh)
            {
                _valueLow = _valueHigh;
                _valueHigh = value;
                ValueHighChanged.InvokeAsync(_valueHigh);
            }
            ValueLowChanged.InvokeAsync(_valueLow);
        }
    }
    [Parameter] public int ValueHigh {
        get => Math.Max(_valueLow,_valueHigh);
        set {
            if (_valueHigh.Equals(value))
                return;
            _valueHigh = value;
            if (_valueLow >= _valueHigh)
            {
                _valueHigh = _valueLow;
                _valueLow = value;
                ValueLowChanged.InvokeAsync(_valueLow);
            }
            ValueHighChanged.InvokeAsync(_valueHigh);
        }
    }
    [Parameter] public EventCallback<int> ValueLowChanged { get; set; }
    [Parameter] public EventCallback<int> ValueHighChanged { get; set; }
    string ID = Guid.NewGuid().ToString().Replace("-", "").Substring(15);
}

推荐阅读