2016-06-11 35 views
5

Ho due ingressi di intervallo sovrapposti, questo crea un effetto di ingresso a più gamme.Ingressi intervallo sovrapposti. Al clic cambia l'input con il valore più vicino

Lo voglio in modo che ogni volta che si fa clic su uno di questi, l'input con il valore più vicino al valore appena cliccato, viene modificato. Non del tutto sicuro su come procedere.

Come posso fare questo?

(function() { 
 
    "use strict"; 
 

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

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

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

 
    var values = input.getAttribute("value").split(","); 
 
    var max = +input.max || 100; 
 
    var ghost = input.cloneNode(); 
 

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

 
    input.value = values[0] || max/2; 
 
    ghost.value = values[1] || max/2; 
 

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

 
    Object.defineProperty(input, "originalValue", descriptor.get ? descriptor : { 
 
     // Dang 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]; 
 
     }, 
 
     enumerable: true 
 
     }); 
 
    } 
 

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

 
    input.addEventListener("input", update); 
 
    ghost.addEventListener("input", update); 
 

 
    update(); 
 
    } 
 

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

 
    if (document.readyState == "loading") { 
 
    document.addEventListener("DOMContentLoaded", multirange.init); 
 
    } else { 
 
    multirange.init(); 
 
    } 
 

 
})();
@supports (--css: variables) { 
 
    input[type="range"].multirange { 
 
    -webkit-appearance: none; 
 
    padding: 0; 
 
    margin: 0; 
 
    display: inline-block; 
 
    vertical-align: top; 
 
    width: 250px; 
 
    margin-top: 50px; 
 
    margin-left: 50px; 
 
    background: lightblue; 
 
    } 
 
    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 */ 
 
    G 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); 
 
    } 
 
}
<input type="range" multiple value="10,80" />

+0

forse avrei potuto attirare la vostra attenzione su questo: http://refreshless.com/nouislider/. Guarda l'esempio in alto. Ho inviato un paio di richieste di pull (in un ramo che a un certo punto diventerà stabile) che abilitano la stessa identica funzionalità per più maniglie (facendo clic sul cursore si sposta la maniglia più vicino al clic - anche se due o tre maniglie si sovrappongono (stessa posizione), la maniglia che può muoversi si muoverà). Non sono sicuro che prenderete in considerazione l'utilizzo di un'altra libreria. La licenza è WTFPL. – xnakos

+0

Grazie, ma preferisco usare l'input nativo [tipo = intervallo]. – ditto

+0

OK. Un'altra domanda. Nel tuo esempio la maniglia sinistra non può essere trascinata adesso, sono corretto? – xnakos

risposta

6

Dovrete catturare un evento del mouse sull'elemento e calcolare quanto è vicina alla elevata marcatore contro la bassa marcatore e decidere quale di aggiornamento basata su questo. Inoltre, poiché si tratta di due elementi di input sovrapposti, è necessario passare manualmente l'evento all'input dell'intervallo basso.

Here's my go a creare una tale funzione:

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 = Math.abs(input.valueHigh - clickValue); 
    var lowDiff = Math.abs(input.valueLow - clickValue); 

    if (lowDiff < highDiff && !isInverted || (isInverted && 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", {screenX: evt.screenX, clientX: evt.clientX}); 
    // 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; 
    } 
    else { 
    console.log("move ghost"); 
    // The high value is closer to the click point than the low value 
    // The default behavior is appropriate, so do nuthin 
    } 
} 

ghost.addEventListener("mousedown", passClick); 

ho messo questo codice immediatamente sopra la linea input.addEventListener("input", update); nel campione, e sembra funzionare. Vedi il mio fiddle.

Alcune clausole però:

  • ho testati solo in Chrome. IE potrebbe avere qualche problema in base a come ho replicato l'evento. È può utilizzare un meccanismo diverso da dispatchEvent ... come fireEvent o qualcosa.
  • Inizialmente l'ho codificato assumendo che l'elemento "ghost" tenesse sempre traccia della gamma alta. Da allora ho aggiornato le cose per invertire l'invio dell'evento quando l'elemento fantasma ha il valore più basso - ma l'ho accelerato.
+0

Sono nel mezzo dell'utilizzo di due gamme di input e sono riuscito a farlo funzionare su tutti tranne firefox (v55.0.3). Ho provato la tua soluzione senza fortuna, ma penso che tu sia su qualcosa che potrebbe funzionare se lo modifichiamo un po ' – doz87

2

Ecco qualcosa che è possibile utilizzare. Anche se potresti voler personalizzare lo stile. Sto modificando lo z-index dell'elemento di scorrimento in base alla sua vicinanza al cursore.
JSFiddle

HTML

<input id='a' type='range' /> 
<input id='b' type='range' /> 
<label role='info'></label> 

JS

var a = document.getElementById('a'); 
var b = document.getElementById('b'); 

a.onmousemove = function(e) { 
    MouseMove.call(a, e); 
}; 
b.onmousemove = function(e) { 
    MouseMove.call(b, e); 
}; 

var MouseMove = function(eventArg) 
{ 

    var max = parseInt(a.max), 
     min = parseInt(a.min), 
     diff = max - min, 
     clickPoint = eventArg.offsetX/a.offsetWidth, 
     clickPointVal = parseInt(diff * clickPoint) + min; 

    /* absolute distance from respective slider values */ 
    var da = Math.abs(a.value - clickPointVal), 
     db = Math.abs(b.value - clickPointVal); 

    // Making the two sliders appear above one another only when no mouse button is pressed, this condition may be removed at will 
    if (!eventArg.buttons) 
    { 
     if (da < db) 
     { 
      a.style.zIndex = 2; 
      b.style.zIndex = 1; 
     } 
     else if (db < da) 
     { 
      b.style.zIndex = 2; 
      a.style.zIndex = 1; 
     } 
    } 
    document.querySelector('label').innerHTML = 'Red: ' + a.value + ', Green: ' + b.value + ', X: ' + eventArg.clientX; 
} 

CSS

input { 
    margin: 0px; 
    position: absolute; 
    left: 0px; 
} 

label { 
    display: inline-block; 
    margin-top: 100px; 
} 

#a { 
    z-index: 2; 
} 

#b { 
    z-index: 1; 
} 
+0

Mi piace questa idea e l'ho implementata io stesso per affrontare l'incompatibilità firefox per più intervalli di input ma suggerire alcune modifiche al modo in cui gestisci l'identificazione dell'handle più vicino, proprio come ha fatto @theMadDeveloper, dovresti passare all'utilizzo delle percentuali. Non sono sicuro del motivo per cui stai usando '* 100/160' – doz87

+0

Ho provato a modificare la tua soluzione ma c'è troppo da cambiare quindi dai un'occhiata a questo esempio qui sotto se vuoi vedere qualcosa che è compatibile con browser cross [ Esempio Codepen] (https://codepen.io/doz87/pen/pWbywG) – doz87

+0

FYI - Il mio esempio include una limitazione che il minimo e il massimo non possono essere più del 10% a parte – doz87