I am implementing a double slider for price range.There is a need to show the value when sliding by the user to select specific range.For that,I have used innerHTML which worked fine. But there is an error saying
"Uncaught TypeError: Cannot read property 'nodeType' of null
at NodePart._setText (lit-html.ts:657)
at NodePart.setValue (lit-html.ts:616)
at TemplateInstance.update (lit-html.ts:810)
at render (shady-render.ts:159)
at HTMLElement._applyRender (lit-element.ts:286)
at HTMLElement._propertiesChanged (lit-element.ts:218)
at HTMLElement._flushProperties (properties-changed.js:387)
at HTMLElement._flushProperties (lit-element.ts:232)
at properties-changed.js:334
at MutationObserver.microtaskFlush (async.js:41)".
I have searched a lot and the answer I got was, one is If the function called before loading the DOM and the other is innerHTML is not used in lit-html. Please help me to get rid of that error.
import {LitElement, html} from '@polymer/lit-element';
import {connect} from 'pwa-helpers/connect-mixin.js';
import {store} from '../../redux/store.js';
import {style} from './genie-flex-css.js';
import {repeat} from 'lit-html/lib/repeat';
export class GenieSlider extends connect(store)(LitElement) {
static get is() {
return 'genie-slider';
}
static get properties() {
// TODO: declare the properties here.
return {
minimum: Number,
maximum: Number
};
}
constructor() {
super();
// TODO: Initialize the properties here
this.minimum = 0;
this.maximum = 10000;
}
requestRender() {
this._invalidateProperties();
}
_invalidateProperties() {
this.__isInvalid = true;
super._invalidateProperties();
}
minPrice(e) {
this.dispatchEvent(new CustomEvent('GG:minPriceChanged', {
bubbles: false,
detail: {
event: e,
value: e.currentTarget.value
}
}));
e.stopPropagation();
this.selectedOption = e.currentTarget.value;
this.minimum = this.selectedOption;
this.requestRender();
}
maxPrice(e) {
this.dispatchEvent(new CustomEvent('GG:maxPriceChanged', {
bubbles: false,
detail: {
event: e,
value: e.currentTarget.value
}
}));
e.stopPropagation();
this.selectedOption = e.currentTarget.value;
this.maximum = this.selectedOption;
this.requestRender();
}
_firstRendered() {
}
_render(properties) {
return html `
<style>
${style}
[slider] {
width: 270px;
position: relative;
}
[slider] > div {
position: absolute;
left: 13px;
right: 15px;
height: 5px;
}
[slider] > div > [inverse-left],[slider] > div > [inverse-right] {
position: absolute;
height: 3px;
border-radius: 10px;
background-color: #CCC;
margin: 0 7px;
}
[slider] > div > [inverse-left] {
left: 0;
}
[slider] > div > [inverse-right] {
right: 0;
}
[slider] > div > [range] {
position: absolute;
left: 0;
height: 3px;
border-radius: 14px;
background-color: #d02128;
}
[slider] > div > [thumb] {
position: absolute;
top: -8px;
z-index: 2;
height: 20px;
width: 20px;
text-align: left;
margin-left: -11px;
cursor: pointer;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4);
background-color: #FFF;
border-radius: 50%;
outline: none;
}
[slider] > input[type=range] {
position: absolute;
pointer-events: none;
-webkit-appearance: none;
z-index: 3;
height: 14px;
top: -2px;
width: 100%;
opacity: 0;
}
div[slider] > input[type=range]:focus::-webkit-slider-runnable-track {
background: transparent;
border: transparent;
}
div[slider] > input[type=range]:focus {
outline: none;
}
div[slider] > input[type=range]::-ms-thumb {
pointer-events: all;
width: 28px;
height: 28px;
border-radius: 0px;
border: 0 none;
background: red;
}
div[slider] > input[type=range]::-moz-range-thumb {
pointer-events: all;
width: 28px;
height: 28px;
border-radius: 0px;
border: 0 none;
background: red;
}
div[slider] > input[type=range]::-webkit-slider-thumb {
pointer-events: all;
width: 28px;
height: 28px;
border-radius: 0px;
border: 0 none;
background: red;
-webkit-appearance: none;
}
div[slider] > input[type=range]::-ms-fill-lower {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-fill-upper {
background: transparent;
border: 0 none;
}
div[slider] > input[type=range]::-ms-tooltip {
display: none;
}
[slider] > div > [sign] {
opacity: 0;
position: absolute;
margin-left: -11px;
top: -39px;
z-index:3;
background-color: #d02128;
color: #fff;
width: 28px;
height: 28px;
border-radius: 28px;
-webkit-border-radius: 28px;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
text-align: center;
}
[slider] > div > [sign]:after {
position: absolute;
content: '';
left: 0;
border-radius: 16px;
top: 19px;
border-left: 14px solid transparent;
border-right: 14px solid transparent;
border-top-width: 16px;
border-top-style: solid;
border-top-color: #d02128;
}
[slider] > div > [sign] > span {
font-size: 10px;
font-weight: 700;
line-height: 28px;
}
[slider]:hover > div > [sign] {
opacity: 1;
}
}
</style>
<div slider id="slider-distance">
<div>
<div inverse-left style="width:70%;"></div>
<div inverse-right style="width:70%;"></div>
<div range style="left:${this.minimum? (this.minimum/100)+'%': 0+'%' };right:${this.maximum? 100 - (this.maximum/100)+'%': 0+'%' }"></div>
<span thumb style="left:${this.minimum? (this.minimum/100)+'%': 0+'%' }"></span>
<span thumb style="left:${this.maximum? (this.maximum/100)+'%': 100+'%' }"></span>
<div sign style="left:${this.minimum? (this.minimum/100)+'%': 0+'%' }">
<span id="minResult">${this.minimum}</span>
</div>
<div sign style="left:${this.maximum? (this.maximum/100)+'%': 100+'%' }">
<span id="maxResult">${this.maximum}</span>
</div>
</div>
<input type="range" value="0" max="${this.maximum}" min="0" step="100" oninput="
this.value=Math.min(this.value,this.parentNode.childNodes[5].value-500);
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[1].style.width=value+'%';
children[5].style.left=value+'%';
children[7].style.left=value+'%';children[11].style.left=value+'%';
children[11].childNodes[1].innerHTML=this.value;" onchange="${e => {this.minPrice(e)}}"/>
<input type="range" value="10000" max="10000" min="${this.minimum}" step="100" oninput="
this.value=Math.max(this.value,this.parentNode.childNodes[3].value-(-500));
let value = (this.value/parseInt(this.max))*100
var children = this.parentNode.childNodes[1].childNodes;
children[3].style.width=(100-value)+'%';
children[5].style.right=(100-value)+'%';
children[9].style.left=value+'%';children[13].style.left=value+'%';
children[13].childNodes[1].innerHTML=this.value;" onchange="${e => {this.maxPrice(e)}}"/>
</div>
`;
}
_stateChanged(state) {
// TODO Set the state dependent properties here
}
}
customElements.define(GenieSlider.is, GenieSlider);
You should be using LitElement's template binding syntax for both handling the input
event (@input=${...}
) and for rendering the selected value in your template. In short:
render() {
return html`
<input type="range"
value=${this.selectedValue}
@input=${this._handleRangeChange}>
Selected value: ${this.selectedValue}
`;
}
_handleRangeChange(event) {
this.selectedValue = event.target.valueAsNumber;
}
You can see this working in the full snippet below:
const { LitElement, html } = litElement;
class MyTestElement extends LitElement {
static get properties() {
return {
selectedValue: { type: Number },
};
}
constructor() {
super();
this.selectedValue = 0;
}
render() {
return html`
<input type="range"
min=0
max=10000
step=100
value=${this.selectedValue}
@input=${this._handleRangeChange}>
Selected value: ${this.selectedValue}
`;
}
_handleRangeChange(event) {
this.selectedValue = event.target.valueAsNumber;
}
}
customElements.define('my-test', MyTestElement);
<script src="https://bundle.run/lit-element@2.2.1"></script>
<my-test></my-test>