Getting Slider Value - d3/dc - javascript

I am currently using dc to make a few line graphs. I also just added in a slider(i played with d3.slider, but then found a different solution)
I am hoping to use the slider to filter the other graphs. Does anyone have suggestions on how to get the selected value from the slicer, and then filter?
To filter I was thinking something like:
d3.select("input[type=range]").on("change", function() {
var gendersel = this.value;
});
var data = _.where(data2, {variable: gendersel});
However, this doesn't work...
I'm also thinking about using crossfilter (probably smarter) for filtering, but still having issues getting the slider value:
var bytest = ndx.dimension(function(d) { return d.testvar; });
bytest.filter([gendersel, gendersel + .5])
My Slider:
<div style="font-size:14px;font-family:sans-serif">Test Filter:
<input style="position:relative;top:4px;" type="range" min="1" max="2" value="1"></div>
I was using this as a reference:
https://www.biostat.wisc.edu/~kbroman/D3/slide_scatter.html

var gendersel isn't in the global scope. You need to define it globally, to use it globally, and not inside the function:
var gendersel;
$("input[type=range]").on("change", function() { //I'm not sure what the select() thing is, I just always use $() for selectors, so if you know d3.select works, then use it.
gendersel = this.value;
});
var data = _.where(data2, {variable: gendersel});
A second potential problem:
If the slider is built by Javascript, then it's possible that the element doesn't exist when this selector is created for your onchange event. If this is the case you will have to create your change event like so:
$(".parentOfInputs").on("change", "input[type=range]", function() {
gendersel = $(this).value; //also not sure if in your case it should be $(this) or this. Try both.
});
The parentOfInputs need to be some element that already existed before the onchange event is created.

Related

Creating Multiple sliders using rangeslider.js plugin

I am working on a photo editing page. The goal is for users to be able to adjust parameters such as hue, saturation, brightness by uploading pictures and then editing them using range sliders. In order to ensure the program worked well on mobile browsers I decided to use the rangeslider.js library. After getting an initial slider up and working I decided to try more advanced designs. I found this design online and like it. My goal now is to have multiple slider each which displays its value like the linked slider. I am having trouble achieving this. To keep things simple I am currently formatting my slider using the standard css from rangeslider.js. I am able to create a single slider which behaves like the one in the linked design, but now I want to generalize this to multiple sliders, which is where I am having trouble. In the single design the code relevant to creating the rangeslider is
//custom slider javascript
var $element = $('input[type="range"]');
var $handle;
$element
.rangeslider({
polyfill: false,
onInit: function() {
$handle = $('.rangeslider__handle', this.$range);
updateHandle($handle[0], this.value);
}
})
.on('input', function() {
updateHandle($handle[0], this.value);
});
function updateHandle(el, val) {
el.textContent = " " + "$" + val + " ";
}
$(document).ready(function(){
//when slider changes, hide start message
$("input").on("change", function() {
$("#helper").fadeOut("slow");
});
//promo-box
$("#js-promo-box").hide();
$("#promo-link").on("click", function(){
$("#js-promo-box").slideToggle();
return false;
});
});
If I leave this function unchanged when declaring multiple range sliders in the html code then there are no errors but only the range input which was declared last will have a label displaying its value. But all of the sliders will be able to change this value. I narrowed this problem down to the fact that there is only a single $handle variable. To fix this I created the code below
<script>
//custom slider javascript
var $element = $('input[type="range"]'); // Gets all the elements of type range
var $handle = new Array($element.length);
console.log($element);
for(i=0; i<$element.length;i++){
console.log($element[i]);
var $temp = $($element[i]);
$temp.rangeslider({
polyfill: false,
onInit: function() {
$handle[i] = $('.rangeslider__handle', this.$range);
updateHandle($handle[i][0], this.value);
}
})
.on('input',function() {
updateHandle($handle[i][0], this.value);
});
}
function updateHandle(el, val) {
console.log(el);
el.textContent = " " + "$" + val + " ";
}
$(document).ready(function(){
//when slider changes, hide start message
$("input").on("change", function() {
$("#helper").fadeOut("slow");
});
//promo-box
$("#js-promo-box").hide();
$("#promo-link").on("click", function(){
$("#js-promo-box").slideToggle();
return false;
});
});
</script>
Now $handler is an array with as many entries as there are range inputs. When run everything initializes fine and the initial values of the sliders are displayed as expected. Despite this when I change the slider I get the error "Uncaught TypeError: can't access property 0, $handle[i] is undefined". This occurs in
.on('input',function() {
updateHandle($handle[i][0], this.value);
});
I do not understand why this error is occurring, especially since the values are initially displayed. Considering the only difference between the code for a single slider and multiple sliders is the $handle is now an array I believe it must have something to do with this but I am unsure where exactly the issue is.
Thank You
It turns out that this is a variable scope problem more than anything. An incredibly useful resource to explain this problem and its solution is "JavaScript Callbacks Variable Scope Problem" by Itay Grudev.
Essentially because the callback variable is created in a for loop use of the variable i (the looping variable) is not a good idea. This is because on later calls the value of i is not known to the callback function because it is outside of its scope. There are multiple ways to fix this as described by Itay Grudev. The easiest is to change
for(var i=0; i<$element.length;i++){ ...
Into
for(let i=0; i<$element.length;i++){ ...
But I believe this is not supported on all browsers. Instead what I did was create an inline closure. This allows me to create a variable within the closure which contains the value of i for a specific slider. In this way each callback can know which slider it corresponds to and react accordingly. My final code is below
for(var i=0; i<$element.length;i++){
console.log($element[i]);
var $temp = $($element[i]);
$temp.rangeslider({
polyfill: false,
onInit: function() {
//On init this is fine I don't need a separate definition of i
$handle[i] = $('.rangeslider__handle', this.$range);
updateHandle($handle[i][0], this.value);
}
})
.on('input', (function() {
var j = i;
return function() {
updateHandle($handle[j][0], this.value);
}
})() );
Now instead of calling a function to adjust the slider values this script creates a function to create a new function and then calls that function. The created function has access to variable j, which is what was missing previously

How to access an HTML element that is dynamically filled with values?

I've read lots of stackoverflow questions with no luck. My problem is, I have an HTML page in which I have
<select id="myid"></select>
and there, there's a Firebase command that retrieves names of values i need, and put it inside the <option> like HERE:
reference.on("child_added", function (childSnapshot){
var key = childSnapshot.key;
var opt = document.createElement('option');
opt.textContent = key;
document.getElementById('myid').appendChild(opt);
});
Now, i need to somehow access these values, that by the way are correctly appearing in both HTML and my site, however:
var val = document.getElementById('myid').value;
console.log(val);
It always returns blank in console. I don't know how else can I access it. Whenever I type those values in <option> by myself in HTML, everything works as it should and console returns the names that are in database.
#edit: as far as i tried to crack it, it seems to do with the fact that javascript cannot access elements, that for javascript itself aren't yet loaded, but i tried doing window.onload and other similar ones and they don't help.
You can use AngularJS, with directive $scope.watch, i will write a simple example of how to use and the link of documentation, if you have any question talk back to me!
function MyController($scope) {
$scope.myVar = 1;
$scope.$watch('myVar', function() {
alert('hey, myVar has changed!');
});
$scope.buttonClicked = function() {
$scope.myVar = 2; // This will trigger $watch expression to kick in
};
}
AngularJS Documentation
I hope help with this :)

Binding multiple events to elements stored in variable

I know that puting reference of HTML element into the variable is a good practice if I need to reference to this element many times. But I run into the problem with this while making my project. How can I bind multiple and the same events to the elements which are stored into the variable?
For now I deal with it this way:
var producerEl = $("#js-producer");
var brandEl = $("#js-brand");
var seriesEl = $("#js-series");
bind(seriesEl);
bind(brandEl);
bind(seriesEl);
function bind($el) {
$el.on("keypress", function () {
// some code..
});
}
I need something like $(producerEl, brandEl, seriesEl).on...
var producerEl = $("#js-producer");
var brandEl = $("#js-brand");
var seriesEl = $("#js-series");
producerEl.add(brandEl).add(seriesEl).on("click", function () {
alert('hello');
});
If you are trying to keep your code readable, might I suggest this approach?
$("#js-producer, #js-brand, #js-series").on('keypress', function () { });
Hmm. If you're using these selectors only one, don't care about "I know it is good to". The best solution is the one provided by David Smith.
Anyway, jQuery is using the sizzle selector engine, who has it's own cache. You can ask for
$("#js-producer, #js-brand, #js-series")
the result would be cached and reused.

What is the best way to identify the updater of an observable

Given any knockout example, I want to be able to identify the source of an observable update.
Consider for example this simple code:
HTML
<input type='text' data-bind='value: someValue' />
<span data-bind='text: someValue'></span>
<button data-bind='click: updateValue'>Update</button>
js
var vm = function () {
var self = this;
this.someValue = ko.observable('random value');
this.updateValue = function () {
self.someValue('random value ' + Math.round(Math.random()*10));
}
}
var vmi = new vm();
vmi.someValue('other random value');
ko.applyBindings(vmi);
Fiddle
I want to know if someValuewas last updated via the input tag, the button, or via code.
What do you think would be the best way to do so? (extender, custom binding handler, ...)
Create separate "writable computed observables" for each of those things to modify. See http://knockoutjs.com/documentation/computedObservables.html#writeable_computed_observables. In your write methods for each of these, you can handle differentiation/coordination/syncing between them as appropriate. There might be a better solution though if you described your actual scenario.

How to make variable accessible within jQuery .each() function?

This is and example of a frequent dilemma: how to make markup accessible inide this .each()?
I'm more interested in learning how to access outer variables from within a closure than I am in this specific issue. I could fix this problem by assigning markup from inside the each function, but I'd rather learn a more elegant way to handle this kind of problem.
// hide form & display markup
function assessmentResults(){
// get assessment responses
var markup = parseForm();
// show assessment results to user
$('#cps-assess-form fieldset').each( function() {
var q = $(this).find('.fieldset-wrapper');
var i = 0;
// hide form questions
q.slideUp();
// insert markup
$('<div>'+markup[i]+'</div>').insertAfter(q);
i++;
});
}
Read the docs, it already has an index!
.each( function(index, Element) )
No need for i
$('#cps-assess-form fieldset').each( function(index) {
var q = $(this).find('.fieldset-wrapper').slideUp();
$('<div/>').html(markup[index]).insertAfter(q);
});
The reason why yours is failing is the i is inside of the function so it is reset every iteration. You would need to move it outside of the function for it to work.

Categories