JQuery - adding value to input, but one input has no ".value" property - javascript

So I want to have a function, that writes to an input like a real human (by triggering all the events)
This is the code I wrote:
Take a close look to the part "element.value += text_array[i]".
function FillInText(element_id, text){
var element = $("#"+element_id);
element.focus();
element.click();
var kdown = jQuery.Event("keydown");
var kup = jQuery.Event("keyup");
var text_array = text.split("");
for(i = 0; i < text_array.length; i++){
var code = text_array[i].charCodeAt(0);
kdown.which = code;
kup.which = code;
element.trigger(kdown);
element.change();
element.value += text_array[i];
element.trigger(kup);
}
element.blur();
}
This code is perfect for any input and it works 99% of the time. But there is one input field, whose value isn't stored in its ".value" property. When I try to set/get the value using that, nothing returns.
However, I CAN get it using JQuery's "val()" function.
But whats perfect about "Element.value" is that you can ADD some value to the existing one. As far as I know, you can't do that with "val()".
Althought you could use:
val(function(index,currentvalue){
return currentvalue + text_array[i];
});
The code undestands it like:
previousValue is "a" and addedValue is "b".
The input field was "a", it takes the "b" and adds it together, then puts in into the input. so it doesn't ADD "a", but rather deletes the existing and then adds "ab" to it. When using "element.value += 'b'", it doesn't even touch the previousValue, instead it just adds it to it.
I would want a function that's like:
element.val() += "b";
I hope you can understand my problem... I'm sorry if I have explained it badly.

I don't think your code should work for any inputs.
.value is a DOM property, but element contains a jQuery object, not a DOM element. You can get the corresponding DOM element by indexing it:
element[0].value += test_array[i];
You can add to the value with jQuery .val() by using a function:
element.val(function(_, old_value) {
return old_value + test_array[i];
}

In general jQuert's selector like $("#element") is an array contains element(s), e.g. [DOMElement, DOMElement,...] and to use pure Java Script properties or methods use $("#element")[0]
https://www.w3schools.com/jquERY/jquery_ref_selectors.asp
But in pure JS document.getElementById("element") is pure element object.
https://www.w3schools.com/jsref/dom_obj_all.asp
Check this
//jQuery's prototype
jQuery.fn.FillInText = function(text) {
var el = $(this),kc,kd,ku
el.focus().click();
for(var i=0;i < text.length;i++) {
kc = text.charCodeAt(i);
kd = jQuery.Event("keydown", { keyCode: kc });
ku = jQuery.Event("keyup", { keyCode: kc });
el.trigger(kd);
el.change();
el[0].value += text[i]; // + "b"
el.trigger(ku);
}
}
//Test listening to keyup, keydown events
$("#inp").on("keydown keyup", function(e) {
console.log(e.type + ": " + String.fromCharCode(e.keyCode))
})
//Go..
$("#inp").FillInText("This is test string")
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" id="inp" />

Related

How do you make an IIFE remember **each** instance of a variable when dealing with event listeners?

I'm working with a list of images that may change in number, so fixed IDs and event listeners are not practical. The below code produces the correct number of buttons with the correct IDs, but only the last one has a functional event listener.
for (var i = 0; i < amount; i++) {
!function(index) {
if (items[index].classList.contains('current')) {
document.getElementById('selectButtons').innerHTML += '<button id=\"bitems' + index + '\"> ⬤ <span class=\"offscreen\">Item ' + i + '</span></button>';
}
else
{
document.getElementById('selectButtons').innerHTML += '<button id=\"bitems' + index + '\"> ◯ <span class=\"offscreen\">Item ' + i + '</span></button>';
}
document.getElementById('bitems' + index).addEventListener("click", function(ev) {
alert("clicked");
});
}(i);
}
Apparently the IIFE is not storing the individual variables like it is supposed to, but I can't figure out why. After all, that is the entire purpose of an IIFE within a loop.
Any help would be greatly appreciated.
IIFE is working fine. Actually every time you update the innerHTML for selectButtons, the DOM is recreated, and all the events attached to it are gone!
Instead of updating the innerHTML in each iteration, you can append the buttons to it instead like:
for (var i = 0; i < 10; i++) {
!function(index) {
var button = document.createElement("BUTTON");
var t = document.createTextNode("Button" + index);
button.appendChild(t);
document.getElementById('selectButtons').appendChild(button);
button.addEventListener("click", function(ev) {
alert("clicked +" +index);
});
}(i);
}
Please do add the conditions around it that you need.
Every time you do innerHTML += you are replacing the entire HTML, which removes any previously installed event handlers. This is one perfectly good reason not to treat HTML as a bunch of strings that you innerHTML onto the page. Instead of strings, think in terms of elements, as in another answer. Then you also don't need to use IDs as a poor man's "variable name" to reference elements; you can just use the element itself.
You don't need a clumsy IIFE. That's what let is for.
Here's a cleaned-up version of your code:
var buttons = document.getElementById('selectButtons');
for (let i = 0; i < amount; i++) {
var current = items[i].classList.contains('current');
var button = document.createElement("BUTTON");
var bullet = document.createTextNode(current ? '◯' : '⬤')
var span = document.createElement('span');
van spanText = `Item ${i}`;
span.className = 'offscreen';
span.appendChild(spanText);
button.appendChild(bullet);
button.appendChild(span);
buttons.appendChild(button);
button.addEventListener('click', () => alert(`clicked ${i}`));
}
If you want to save a line or two, you could take advantage of the fact that appendChild returns the appended child, and chain:
buttons.appendChild(button).appendChild(span).appendChild(spanText);
If you're going to be doing a lot of this, it would be best to create some tiny utility routines:
function createElementWithText(tag, text) {
var b = document.createElement(tag);
var t = document.createTextNode(text);
b.appendChild(t);
return b;
}
function button(text) { return createElementWithText('button', text); }
function span(text) { return createElementWithText('span', text); }
Now you can write your code more concisely as:
var buttons = document.getElementById('selectButtons');
for (let i = 0; i < amount; i++) {
var current = items[i].classList.contains('current');
var button = button(current ? '◯' : '⬤');
var span = span(`Item ${i}`);
span.className = 'offscreen';
buttons.appendChild(button).appendChild(span);
button.addEventListener('click', () => alert(`clicked ${i}`));
}
Actually, it would moderately preferable to create a document fragment, add all the buttons to it in advance, then insert it into the DOM a single time.
However, in practice, you would be better off using some kind of templating language, in which you could write something like:
<div id="selectButtons">
{{for i upto amount}}
<button {{listen 'click' clicked}}>
{{if items[i] hasClass 'current'}}◯{{else}}⬤{{endIf}}
<span class="offscreen">Index {{i}}</span>
</button>
{{endFor}}
</div>
It's beyond the scope of this answer to recommend a particular templating language. There are many good ones out there, such as Mustache, that google can help you find, with a search such as "javascript templating languages".

Difference in js native .value vs jQuery .val()

I have a quick question about the difference in JavaScript's native element.value vs jQuery's $(element).val();
I have created a BB editor in AngularJS and this is where my question came from.
Here is the code for my directive:
bbApp.directive('bbEdit', function() {
return {
restrict: 'A',
templateUrl: 'tpl/editor.html',
link: function(scope) {
scope.tagType = '';
var el = document.querySelector('#bbeditor');
scope.wrap = function(type) {
scope.tagType = type;
var str = el.value.toString();
var selection = str.slice(el.selectionStart, el.selectionEnd);
if( scope.noWrapTags.indexOf(scope.tagType) == -1 && selection == "" ) {
alert('Please select text to format');
return false;
}
if( scope.allowedTags.indexOf(scope.tagType) == -1 ) {
alert('Sorry, that formatting option is not available');
return false;
}
var strArr = str.split("");
if( scope.noWrapTags.indexOf(scope.tagType) != -1 ) {
strArr.splice(el.selectionStart,selection.length, "["+ scope.tagType +"]");
} else {
strArr.splice(el.selectionStart,selection.length, "["+ scope.tagType +"]"+selection+"[/"+ scope.tagType +"]");
}
el.value = strArr.join("");
}
}
}});
I am accessing the element like so: var el = document.querySelector('#bbeditor');
then getting the value: var str = el.value.toString();
But when I attempt to do this using jQuery's .val() it's not working properly.
The way it is currently written, the app will wrap whatever highlighted text in the appropriate custom bb tag.
But when I access the value of the textarea like this:
var el = angular.element(element).find('textarea');
var str = el.val().toString();
I get the text value but my string manipulation simply wraps the entire value of the textarea with the bb tag as opposed to wrapping just the text highlighted by the user.
Is it even worth the hassle to use jQuery for this? Is my use of document.QuerySelector() okay?
I was just wanting to use what features are available in Angular, and obviously in my directive I can access element. But the jQuery .val() is not working the same as native .value.
Any explanation/advice would be appreciated. I am new to Angular.
My app is working, but I am just wondering if there is a different way I should be doing this.
document.querySelector returns the first element that matches. jQuery's .find method returns an array of matching elements.
Besides that, .value isn't returning the selected text, you are using var selection = str.slice(el.selectionStart, el.selectionEnd) to get the selected text.
If your code works in javascript, I'd leave it as such. There isn't a compelling reason to convert it to jQuery (IMHO). You won't find anything that makes things overwhelmingly easier -- in this case.

jQuery getting sum with keypress always one step behind

I am trying to create an calculator in which I want add/sum on every key press I make like this:
$('#padd').keypress(function() {
var ypoints = "200";
var points = parseInt( $(this).val() );
console.log( "Points: " + (points + parseInt(ypoints)) );
});
My issue is that it seems like "points" always is one step behind. Lets say that my "ypoints" is 200 and I type in first 1, the console.log says "NaN"? If I then type in a "10" the console.log says "201" while in fact it should say "210"?!? If I then add an "100" in the add field it says "210" but it should say "300". Always one keypress behind?!?
What am I missing here or is this not the correct way to do this?
Any help is appreciated and thanks in advance :-)
Try using the keyup function instead (although you might not want to listen for key* events at all, see below):
$('#padd').keyup(function(){
var ypoints = 200;
var points = parseInt($(this).val());
console.log("Points: "+(points+ypoints));
});
Instead of keyup, you should listen for the explicit input event:
$('#padd').on("input", function(){
var ypoints = 200;
var points = parseInt($(this).val());
console.log("Points: "+(points+ypoints));
});
An example when listening for the input event trumps using keyup is when using an input of type number. In a (not so) modern browser, this adds a stepper to the input field, which you can use to increment or decrement the inputs value by 1 using your mouse. This does not fire the keyup event (no keys are pressed) but nevertheless changes the value.
It is really a matter of when certain events are fired and the state of the input's value at that time. There is an explanation on quirksmode.org:
keyup
Fires when the user releases a key, after the default action of that key has been performed.
and more information on the input event on MDN:
The DOM input event is fired synchronously when the value of an or element is changed.
Try this, its a working code : Fiddle
$('#padd').keyup(function(){
var ypoints = "200";
var points = parseInt($(this).val());
alert("Points: "+(points+parseInt(ypoints)));
});
You can also code this way...
function GetTextboxAmount(obj) {
var retVal = 0;
try {
if (obj.val() == "") {
retVal = 0;
} else if (obj.val() == '') {
retVal = 0;
} else if (isNaN(parseFloat(obj.val()))) {
retVal = 0;
} else {
retVal = Math.round(parseFloat(obj.val()));
}
} catch (e) { retVal = 0; }
obj.val(retVal);
return retVal;
}
$('#padd').keyup(function(){
var ypoints = "200";
var points = GetTextboxAmount($(this));
console.log("Points: "+(points+parseInt(ypoints)));
});
Problem in ParseInt function.
try this Code:
$('#padd').keypress(function(){
var ypoints = "200";
var points = parseInt($(this).val());
console.log("Points: "+parseInt(ypoints+points));
});

even.data is not change

here is my own practice demo, it works well as i expect. but when i use line 8 ( comment on my demo code ) instead of line 7 ,the input text values all change to 0 which is different than the result of demo i giving out.
i look at the jquery website it only gives me this
Description: An optional object of data passed to an event method when the current executing handler is bound.
i think the result of using line 8 or line 7 should be the same because of i is assigned to count object, but it is not. Could someone explain me about this question. by the way, if someone could refactor my code will be even more nicer THANKS!!
here is my code
var i = 0;
$("#aa").on("click", {
count : i
},
function(event) {
var div = $('<div/>');
var input = $('<input />').attr("value", event.data.count);
event.data.count++;
//i++;
var bt = $('<input />').attr({
type : "button",
value : "remove",
});
div.append(input);
div.append(bt);
var index = $("div").length;
if (index == 0) {
$("#aa").after(div);
} else {
$("div").last().after(div);
}
bt.on('click', function(event) {
$(this).parent().remove();
});
});
when the current executing handler is bound.
this is important thing in the section. So, the object will be created only once, during the time of binding the function with the click event. So, changing i will not change the value of event.data.
When you do
event.data.count++;
you are actually mutating the object and the state of the object will be retained. That is why it works.

Using JavaScript & jQuery in a single function (Nodes & Stuff)

I am currently learning jQuery. I know that jQuery is a custom library for JavaScript.
I am doing some learning examples in a book that is only using JavaScript, and to further my learning experience, I am trying to make use of jQuery for anything that might be more efficient.
So, I have this code:
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
if (document.getElementById('currentWord').childNodes.length > 0) {
$('#currentWord p').append(letter);
} else {
var p = document.createElement('p');
var txt = document.createTextNode(letter);
p.appendChild(txt);
$('#currentWord').append(p);
}
}
Question #1:
If I change document.getElementById('currentWord').childNodes.length to $('#currentWord').childNodes.length it doesn't work. I thought the jQuery selector was the same thing as the JS document.getElementById as that it brought me back the DOM element. If that was the case, it'd make sense to be able to use the .childNodes.length functions on it; but it doesn't work. I guess it's not the same thing?
Question #2:
The code is textbook code. I have added all the jQuery that there is in it. My jQuery knowlede is limited, is there a more efficient way to execute the function?
The function's purpose:
This function is supposed to create a p element and fill it with a Text Node if it's the first time it's run. If the p element has already been created, it simply appends characters into it.
This is a word generating game, so you click on a letter and it gets added to a 'currentWord' div. The tile's letter is embedded in the 3rd css class, hence the attr splitting.
Thanks!
document.getElementById('currentWord')
returns a DOM object whereas $('#currentWord') returns a DOM object wrapped inside a jQuery object.
To get the plain DOM object you can do
$('#currentWord').get(0)
So
$('#currentWord').get(0).childNodes.length
should work.
Question #1:
jQuery returns a jQuery object. To return it to a regular javascript object use $(object)[0] and you can then treat it as a plain javascript (or DOM) object.
Question #2:
The efficiency looks good to me. Although you might want to use spans instead of p elements.
I guess one thing you could do (even though yours looks to run very fast) is cache the dom element:
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
var currentWord = document.getElementById('currentWord');
if (currentWord.childNodes.length > 0) {
$(currentWord).find('p').append(letter);
} else {
var p = document.createElement('p');
p.innerHTML = letter;
currentWord.appendChild(p);
}
}
Calls to the jQuery() function ($()) return a jQuery object containing the matching elements, not the elements themselves.
Calling $('#some-id') will, then, return a jQuery object that contains the element that would be selected by doing document.getElementById('some-id'). In order to access that element directly, you can get it out of that jQuery object, using either the .get() function or an array index syntax: $('#some-id')[0] (it's 0-indexed).
I think you can replace all of this with a call to the text function.
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
var currentWordP = $('#currentWord p');
if (currentWordP.size() > 0) {
currentWordP.text(currentWordP.text() + letter);
} else {
$('#currentWord').append("<p>" + letter + "</p>");
}
}
1: Use $.get(0) or $[0] to get the DOM element. e.x. $('#currentWord')[0].childNodes.length.
2: Try this:
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
if ($('#currentWord p').length > 0) {
$('#currentWord p').append(letter);
} else {
$('#currentWord').append(
$('<p />', { text: letter })
);
}
}
Question #1:
document.getElementById returns DOM object. more
childNodes.length is property of Node object which is returned by document.getElementById.
jQuery selector returns jQuery object more. You can get DOM object from jQuery object using .get
$('#IDselector').get(0) = document.getElementById('IDselector')
Question #2:
function addLetter(foo) {
$(foo).unbind('click');
var tileLetter = $(foo).attr('class').split(' ');
var letter = tileLetter[2].charAt(1);
if ($('currentWord p').length > 0) {
$('#currentWord p').append(letter);
} else {
var p = $('<p />').text(letter);
$('#currentWord').append(p);
}
}

Categories

Resources