I have an implementation I am trying to do with inputs which have the type range. These will be 2 inputs of type range, and I'm trying to get a 'double range' kind of implementation out of this. I am using Lit element for this, and my problem currently, is that I am trying to prevent one slider to go over the other one. So the 'min' range should not exceed the 'max' range and vice versa. I can't get this to work with only Lit reflective properties.
I have the feeling that the value binding I'm doing on the range inputs is just not triggering the exact same logic behind the screens as a native JavaScript way would.
so these are my properties:
static get properties() {
return {
valueMax: { type: Number, attribute: "max-value", reflect: true },
valueMin: { type: Number, attribute: "min-value", reflect: true },
info: { type: String, attribute: "info" },
min: { type: Number, attribute: "min" },
max: { type: Number, attribute: "max" },
step: { type: Number, attribute: "step" },
disabled: { type: Boolean, attribute: "disabled" },
required: { type: Boolean, attribute: "required" },
invalidMin: { type: Boolean, attribute: "min-invalid", reflect: true },
invalidMax: { type: Boolean, attribute: "max-invalid", reflect: true },
};
}
These are my inputs:
<input
type="range"
name="min"
min="${this.min}"
max="${this.max}"
.value="${this.valueMin}"
step="${this.step || 10}"
title="min"
disabled=${this.disabled || nothing}
@input=${(e) => this._updateValue(e, "min")}
/>
<input
type="range"
name="max"
min="${this.min}"
max="${this.max}"
.value="${this.valueMax}"
step="${this.step || 10}"
disabled=${this.disabled || nothing}
title="max"
@input=${(e) => this._updateValue(e, "max")}
/>
and in the updateValue method, I try to do this:
_updateValue(e, inputType) {
let newValue = parseFloat(e.target.value);
console.log(e.target);
// Prevent one slider from going beyond the other
if (inputType === "min") {
this.valueMin = newValue >= this.valueMax ? this.valueMax : newValue;
//without this line, it doesn't work...
e.target.value = this.valueMin;
} else if (inputType === "max") {
this.valueMax = newValue <= this.valueMin ? this.valueMin : newValue;
//without this line, it doesn't work...
e.target.value = this.valueMax;
}
this.updateStyles();
}
So from the code, you can see, if I don't set the value directly to the dom element (as in go through the native JS way) it just doesn't prevent the slider to go over the other one. Am I doing something wrong, or is this a shortcoming of Lit in combination with the type range input?
Your workaround of setting the value directly on the DOM element seems fine for this use case.
The reason you're observing this behavior is due to the optimizations that Lit has to prevent (usually) unnecessary updates.
The way change detection happens with reactive property makes it such that the assignments to
this.valueMinandthis.valueMaxare not triggering a reactive update lifecycle when the conditional assignment results in the same value since previous update. The parent component doesn't re-render so the value bindings to the child input elements do not get applied.One way to fix this behavior "using Lit" is to manually call
this.requestUpdate()within_updateValue()to force an update cycle to happen.In addition to the above, you also need to bypass Lit's diff checking mechanism on the binding itself. Lit uses the previous set value of the component's property rather than the current value on the DOM element to check whether to update the DOM. Using the
live()directive makes it check against the actual live DOM value and update it.The Lit workaround is more work than just directly setting the DOM value so I'd just go with the way you have it, unless you have other places where the property values get programmatically updated that require the binding to take effect.