jQuery extend plugin settings with data attributes - javascript

I have managed to modify simple number spinner plugin to use data-attribute for settings. Here is the part of the code
(function($) {
$.fn.simpleSpinner = function(options) {
var settings = $.extend({
size: 'large',
step: 1,
}, $(this).data('spinner'), options);
return this.each(function(e) {
var self = $(this);
......
});
};
}(jQuery));
Then I initialize plugin like:
$(function() {
$('.spinner').simpleSpinner();
});
<input class="spinner" type="number" value="1" min="1" max="10" data-spinner='{"size":"large"}'>
<input class="spinner" type="number" value="1" min="1" max="10" data-spinner='{"size":"small"}'>
This all works fine, except that if I have more then one element, then data-attributes are applied to all of them from first element.
I would like to be able to individually control each element using size in data-spinner without the need of having two instances of plugin,
$('.spinner2').simpleSpinner();
with different class assigned to it.

Just insert your setting initialization code where you are looping through the elements
(function($) {
$.fn.simpleSpinner = function(options) {
return this.each(function(e) {
var $self = $(this);
var settings = $.extend({
size: 'large',
step: 1,
}, $self.data('spinner'), options);
$self.after($("<pre/>",{text:JSON.stringify(settings)}))
});
};
}(jQuery));
$(function() {
$('.spinner').simpleSpinner();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input class="spinner" type="number" value="1" min="1" max="10" data-spinner='{"size":"large"}'>
<input class="spinner" type="number" value="1" min="1" max="10" data-spinner='{"size":"small"}'>
<input class="spinner" type="number" value="1" min="1" max="10" data-spinner='{"size":"lorem"}'>
<input class="spinner" type="number" value="1" min="1" max="10" data-spinner='{"size":"ipsum"}'>

Related

How to swap values by changing one of them via javascript?

There are three fields with numbers from 1 to 3. I am trying to make it so if a person uses only the arrows there should always be one "1", one "2", and one "3". Why is it not always working and how could I make it work?
jQuery(document).ready(function($) {
var prevNumber;
$(".numbers").focus(function() {
prevNumber = $(this).val();
}).change(function() {
curNumber = $(this).val();
$('.numbers[value="' + curNumber + '"]:not(:focus)').first().val(prevNumber);
prevNumber = curNumber;
});
});
<input type="number" value="1" min="1" max="3" step="1" class="numbers" />
<input type="number" value="2" min="1" max="3" step="1" class="numbers" />
<input type="number" value="3" min="1" max="3" step="1" class="numbers" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Here is a jsfiddle.
Why that approach doesn't work
The value attribute is not connected to the value of the input. I know that sound surprising. :-) The value attribute is the default value of the input. It doesn't change (unless you use setAttribute("value", x); or .defaultValue = x; to change it).
Your selector uses the attribute:
$('.numbers[value="' + curNumber + '"]')...
So it'll work on inputs whose value hasn't been changed by the user, but will fail once they have, selecting the wrong input.
How you could fix it
You could change the default value as well as the value by setting both defaultValue and value (being sure to update the defaultValue on the one that changed, too), like this (see comments):
jQuery(document).ready(function($) {
var prevNumber;
$(".numbers").focus(function() {
prevNumber = $(this).val();
}).change(function() {
// Get the element wrapper
var $this = $(this);
// Get the current value
var curNumber = $this.val();
// Make sure the default value on this element is updated
this.defaultValue = curNumber;
// Update both the value and default value on the other
// input that used to have this number
$('.numbers[value="' + curNumber + '"]:not(:focus)').first().val(prevNumber).prop("defaultValue", prevNumber);
prevNumber = curNumber;
});
});
<input type="number" value="1" min="1" max="3" step="1" class="numbers" />
<input type="number" value="2" min="1" max="3" step="1" class="numbers" />
<input type="number" value="3" min="1" max="3" step="1" class="numbers" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
What I'd do instead (maybe -- your approach is growing on me)
I think I'd approach it without trying to remember state, e.g., just in the change: Get the number of the one that changed, then assign any other numbers to its siblings. See the comments:
var numbers = $(".numbers").map(function() { return this.value; }).get();
jQuery(document).ready(function($) {
$(".numbers").change(function() {
// Get a wrapper for this input
var $this = $(this);
// Get this number
var thisNumber = $this.val();
// Get the unused numbers
var unused = numbers.filter(function(num) { return num != thisNumber; });
// Assign them to the siblings, in order
$this.siblings().val(function(index) {
return unused[index];
});
});
});
<input type="number" value="1" min="1" max="3" step="1" class="numbers" />
<input type="number" value="2" min="1" max="3" step="1" class="numbers" />
<input type="number" value="3" min="1" max="3" step="1" class="numbers" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
I kept that general, rather than assuming the values would only be 1, 2, and 3 (and rather than assuming there'd only be three numbers).
The problem in your code is that the "value" attribute contains the initial value for the input. When you use the following selector:
$('.numbers[value="' + curNumber + '"]:not(:focus)')
you are selecting the element that initially had the given value, and not has this value now.
Try this selector instead, and all will work fine:
$('.numbers:not(:focus)').filter(function(index, element){
return $(element).val() == curNumber;
})
Here is a jsfiddle. ;)
I'd like to propose you a more elaborate solution that might help you as your application grows: you should store the JS value apart from the <input /> controlling it (this way, adding multiple <input>s or modifying the value from your code becomes easier. The most important thing is that you should have a single trusted data storage independent from the DOM that is always in a valid state (in this case: without duplicates).
Given your problem (the values should be unique and only swaps should be possible), it's easier to handle it as a pure JS problem (and do not try to do everything in jQuery - though I agree it's a great lib, it's not necessarily the best tool for everything).
Here is my commented solution:
jQuery(document).ready(function($) {
// This array the current values of the inputs
var numbers = [1, 2, 3];
// numbers should not be modified directly but trough
// setNumber: this function ensures that numbers is ALWAYS
// a swap of the original value ([1, 2, 3]).
// When a value is set, this function returns the previous
// index of the value
function setNumber(index, newVal) {
// find other index
var prevIndex = numbers.indexOf(newVal);
if (prevIndex < 0) {
alert('Invalid value, please enter 1, 2 or 3');
}
// swap
numbers[prevIndex] = numbers[index];
numbers[index] = newVal;
return prevIndex;
}
// This function updates the inputs to ensure
// that their displayed value match the one stored in numbers
function updateNumbersView() {
$(".numbers").each(function(idx, elem) {
elem = $(elem);
if (parseInt(elem.val(), 10) !== numbers[idx]) {
elem.val(numbers[idx]);
}
});
}
$(".numbers").change(function() {
var self = $(this);
var curNumber = parseInt(self.val(), 10);
var curIndex = $(".numbers").index(self);
if (curNumber === numbers[curIndex]) {
return false; // no change
}
// update model:
var changedIndex = setNumber(curIndex, curNumber);
// updateView:
$('.numbers').eq(changedIndex).val(numbers[changedIndex]);
// or to be more generic (ie. multiple inputs for the same value):
// updateNumbersView();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="number" value="1" min="1" max="3" step="1" class="numbers" />
<input type="number" value="2" min="1" max="3" step="1" class="numbers" />
<input type="number" value="3" min="1" max="3" step="1" class="numbers" />
I would solve this by adding another property to each node.
jQuery(document).ready(function($) {
var $numbers = $('.numbers'),
off = false;
$numbers.each(function () {
this.prev = this.value;
});
$numbers.change(function () {
var source = this;
if (off) return; // this algorithm is already running
off = true;
$numbers.each(function () {
if (this !== source && this.value === source.value) { // if conflict
this.prev = this.value = source.prev; // swap with old value
}
});
this.prev = this.value; // update for next time
off = false; // re-enable
});
});
<input type="number" value="1" min="1" max="3" step="1" class="numbers" />
<input type="number" value="2" min="1" max="3" step="1" class="numbers" />
<input type="number" value="3" min="1" max="3" step="1" class="numbers" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

JavaScript fetching multiple known element IDs

I'm learning JS by making a character sheet (rpg), I got a form set up like this
<fieldset id="char-int">
<label for="int">INT</label>
<input id="int" name="int" placeholder="40" type="number" min="0" max="100">
<input id="int-hard" name="int-hard" placeholder="20" type="number" min="0" max="100">
<input id="int-extr" name="int-extr" placeholder="6" type="number" min="0" max="100">
</fieldset>
I need to change the value in int-hard and int-extr with simple rounded down division.
window.onchange = changevalue;
function changevalue() {
var hardRoll = document.getElementById("int").value / 2;
var extrRoll = document.getElementById("int").value / 5;
var setStat = document.getElementById("str-hard").value = Math.floor(hardRoll);
var setStat = document.getElementById("str-extr").value = Math.floor(extrRoll);
This works, but there must be a smarter way to do this as I got multiple IDs I want to do the same stuff to like STR, DEX etc..
You can remove the ids from your inputs and work within the context of your fieldset, like this:
<fieldset id="char-int">
<label>
INT
<input name="int" placeholder="40" type="number" min="0" max="100">
</label>
<input name="int-hard" placeholder="20" type="number" min="0" max="100">
<input name="int-extr" placeholder="6" type="number" min="0" max="100">
</fieldset>
function changevalue() {
var fieldset = document.getElementById("char-int");
var intField = fieldset.querySelector('["name=int"]');
var intHardField = fieldset.querySelector('["name=int-hard"]');
var intExtrField = fieldset.querySelector('["name=int-extr"]');
// ...
}
(Note that I also moved your name="int" field into the label so we don't have to use an id to link them.)
querySelector finds the first element within the element you call it on that matches the given CSS selector. (There's also querySelectorAll, which finds a list of all matching elements.)
Depending on how much you can parameterize the actual logic of the changevalue function, you could change the names to not have int- in them (or add classes), and then pass in the id of the fieldset (or the fieldset instance itself).
<fieldset id="char-int">
<label>
INT
<input id="int-main" name="main" placeholder="40" type="number" min="0" max="100">
</label>
<input name="hard" placeholder="20" type="number" min="0" max="100">
<input name="extr" placeholder="6" type="number" min="0" max="100">
</fieldset>
function changevalue(fieldSetId) {
var fieldset = document.getElementById(fieldSetId);
var mainField = fieldset.querySelector('["name=main"]');
var hardField = fieldset.querySelector('["name=hard"]');
var extrField = fieldset.querySelector('["name=extr"]');
// ...
}
QS and QSA are supported by all modern browsers, and also IE8.
I got multiple IDs I want to do the same stuff ...
When you here such a phrase, it can be a sign that you need to use classes. They are used exactly for this: to denote group of similar elements.
So what you should do is to add the same class to all elements, then select all necessary elements, and then use for loop to process all of them.
For example, HTML:
<input class="int" name="int" placeholder="40" type="number" min="0" max="100">
<input class="int-hard" name="int-hard" placeholder="20" type="number" min="0" max="100">
<input class="int-extr" name="int-extr" placeholder="6" type="number" min="0" max="100">
and then javascript:
var int = document.querySelectorAll('.int');
for (var i = 0; i < int; i++) {
var hardRoll = int[i].value / 2;
var extrRoll = int[i].value / 5;
}
Try this solution:
// Your inputs, selected by ID
var inputs = document.querySelectorAll("#int-hard, #int-extr");
// Apply onchange to selected input fields
for(var i = 0, length = inputs.length; i < length; i++) {
inputs[i].onchange = function() {
this.value = Math.floor(this.value);
};
}
And please don't use window.onchange as this fires off to every change in your document.

Jquery getting values of slider inside .each loop

I have 3 sliders (they are dynamic, so I need to loop through them).
I have a jsFiddle here: http://jsfiddle.net/mtait/R5czJ/
HTML is:
<label for="slider1">item 1</label>
<input type="range" class="mtslide" name="slider1" id="slider1" min="0" max="10" value="0">
<label for="slider2">item 2</label>
<input type="range" class="mtslide" name="slider2" id="slider2" min="0" max="10" value="0">
<label for="slider3">item 3</label>
<input type="range" class="mtslide" name="slider3" id="slider3" min="0" max="10" value="0">
I am trying to loop through them, and create a JSON string:
function slide() {
var ExtraPrices = [20.00,30.00,50.00];
var ExtraIDs = [1,2,3];
var count = 0;
var arr = [];
$('.mtslide').each(function () {
var obj = {
id: ExtraIDs[count],
price: ExtraPrices[count],
number: $(this).slider("option", "value")
};
arr.push(obj);
count += 1;
});
alert(JSON.stringify(arr));
}
However, "number" or the value of the sliders, is always null:
How do I get the correct value of each slider, within my .each loop above?
thank you,
Mark
jQuery's each function actually gives you two variable: index and Element
http://api.jquery.com/each/
Assuming you want the value of each element you want something like this:
$('.mtslide').each(function (index, Element) {
var obj = {
id: ExtraIDs[index],
price: ExtraPrices[index],
number: $(Element).val()
};
arr.push(obj);
});
Keeping a separate array for price and id can be error prone. You should consider specifying additional values on an html element with data attribute.
http://html5doctor.com/html5-custom-data-attributes/
http://api.jquery.com/data/
Something like this:
<input type="range" class="mtslide" name="slider1" id="slider1" min="0" max="10" value="0" data-price="20.00" data-id="1">
<input type="range" class="mtslide" name="slider2" id="slider2" min="0" max="10" value="0" data-price="30.00" data-id="2">
<input type="range" class="mtslide" name="slider3" id="slider3" min="0" max="10" value="0" data-price="50.00" data-id="3">
Then you can call them more specifically to the element:
$('.mtslide').each(function (index, Element) {
var obj = {
id: $(Element).data("price"),
price: $(Element).data("price"),
number: $(Element).val()
};
arr.push(obj);
});
The complete fiddle:
http://jsfiddle.net/Wwwtv/2/

HTML5 Slider with onchange function

I have a slider (input type range) that is supposed to run a function when the value is being changed. The function should then display the new value in a separate div container. After placing an alert in the function, I know that the function isn't being called, but after googling for an hour and trying a few different methods I just can't find the error.
Here's the HTML part:
<input id="slide" type="range" min="1" max="100" step="1" value="10" onchange="updateSlider(this.value)">
<div id="sliderAmount"></div>
JavaScript:
// Slider
function updateSlider(slideAmount)
{
alert("error");
var sliderDiv = document.getElementById("sliderAmount");
sliderDiv.innerHTML = slideAmount;
}
It works, you just need to make sure that the JavaScript function is defined when the element is rendered, for example:
<script>
function updateSlider(slideAmount) {
var sliderDiv = document.getElementById("sliderAmount");
sliderDiv.innerHTML = slideAmount;
}
</script>
<input id="slide" type="range" min="1" max="100" step="1" value="10" onchange="updateSlider(this.value)">
<div id="sliderAmount"></div>
See this demo: https://jsfiddle.net/Mmgxg/
A better way would be to remove the inline onchange attribute:
<input id="slide" type="range" min="1" max="100" step="1" value="10">
<div id="sliderAmount"></div>
And then add the listener in your JavaScript code:
var slide = document.getElementById('slide'),
sliderDiv = document.getElementById("sliderAmount");
slide.onchange = function() {
sliderDiv.innerHTML = this.value;
}
https://jsfiddle.net/PPBUJ/

Fire oninput event with jQuery

I have an oninput event on a textarea to check the height and resize it. Now I need to edit the value sometimes. I do this just by editting the val() in jQuery, but that does not trigger the oninput event. Is there any way to trigger the oninput event programatically with jQuery?
Use .on('input'). For example:
$('textarea').on('input', function() {
text = $('textarea').val();
$('div').html(text);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea placeholder="Type something here"></textarea>
<div></div>
It is a bit too late, but for future reference, there is the .trigger method.
$("#testArea").on("input", function(e) {
$("#outputArea").text( $(e.target).val() )
});
$("#testArea").val("This is a test");
$("#testArea").trigger("input");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input id="testArea" type="text" />
<div id="outputArea"></div>
You can simply invoke it, e.g.:
$("input")[0].oninput = function () {
alert("hello");
};
$("input")[0].oninput();
...but as #Sammaye points out, jQuery has no explicit "oninput" handler, so you'll have to use POJS.
Demo on JS Fiddle.
oninput is not actually in JQuery yet.
You can see posts about it here:
http://forum.jquery.com/topic/html5-oninput-event
http://bugs.jquery.com/ticket/9121
Basically the general consensus is that they don't want it yet.
But no, changing val() directly would not trigger the html5 oninput because it's specification states it is when the user, in the UI, changes the value of the input.
Edit:
However some one has kindly made a plugin for people who wish to use HTML5 only events: https://github.com/dodo/jquery-inputevent
You can bind to input and change:
input will be triggered at user input
change will be triggered at change() and to val(" ") assignments, but after some changes
$("#textarea").bind("input change", function() {
alert("change happend");
});
...
after you binded to change you can call it manualy on each val(" ") assignment:
$("#textarea").val("text").change();
or you can overwrite jQuery val(" ") method to trigger change on each user val(" ") call:
(function ($) { var fnVal = $.fn.val;
$.fn.val = function(value) {
if (typeof value == 'undefined') {
return fnVal.call(this);
}
var result = fnVal.call(this, value);
$.fn.change.call(this); // calls change()
return result;
};
})(jQuery);
Try with "keypress" or "keydown".
Example 1:
$("#textarea").keypress(function(){
alert($("#textarea").val());
});
Example 2:
$("#textarea").keydown(function(){
alert($("#textarea").val());
});
push RUN CODE SNIPPET for seeing results
i've been searching for a better example to join an input range to an input value and so
i modified Fernando's example in a JQuery plugin ,so :
for every
<input type="range" min="1" max="100" value="50" id="range1">
you'll have his value:
<input type="text" disabled id="value" class="range1" value="0">
so is like for any parent range id="range1" there is a child id="value" class="range1"
<!-- <script src="../js/jquery.js"></script> -->
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
1<input type="range" min="1" max="100" value="50" id="range1"><input type="text" disabled id="value" class="range1" value="0"><br>
2<input type="range" min="1" max="100" value="50" id="range2"><input type="text" disabled id="value" class="range2" value="0"><br>
3<input type="range" min="1" max="100" value="50" id="range3"><input type="text" disabled id="value" class="range3" value="0"><br>
4<input type="range" min="1" max="100" value="50" id="range4"><input type="text" disabled id="value" class="range4" value="0"><br>
...<br>
n<input type="range" min="1" max="100" value="50" id="rangen"><input type="text" disabled id="value" class="rangen" value="0"><br>
<script type="text/javascript">
<!--
$(document).ready(function() {
$('input').on("input",function(){
$('input').each(function(index) {
//console.log('1 '+index + ': ' + $(this).text()+',id='+$(this).attr('id'));
if($(this).attr('id')=='value'){
//console.log('2 class='+$(this).attr('class'));
var obj=$('input#'+$(this).attr('class') );
var hisvalue=$(this);
//console.log('3 parent`s value='+obj.val() );
obj.on("input",function(){
hisvalue.val(obj.val());
});
}
});
});
$('input').trigger("input");
});
//-->
</script>

Categories