Why is this textfield losing focus with morphdom? - javascript

Here's a contrived example below that isolates my problem. There's a simple text field w/ a message that displays if the character count is even. If I move the message below the textfield it seems morphdom sees that only the value of the textfield has changed, so it doesn't rebuild the input node which mean the control keeps focus. If I keep the message above the textfield, morphdom rebuilds the control despite the fact that I'm specifying an id.
From the docs...
https://github.com/patrick-steele-idem/morphdom
In addition, the algorithm used by this module will automatically match up elements that have corresponding IDs and that are found in both the original and target DOM tree.
Code Example...
https://codepen.io/anon/pen/NOvZJz
var text = "";
var origRootDiv = null;
function createView() {
var rootDiv = document.createElement("div");
// If moved under, issue goes away
if (text.length > 0 && text.length % 2 == 0) {
var messageDiv = document.createElement("div");
messageDiv.innerHTML = "Even char count!";
rootDiv.appendChild(messageDiv);
}
// End if
var input = document.createElement("input");
input.id = "this-should-help-right";
input.value = text;
rootDiv.appendChild(input);
input.addEventListener("input", function(e) {
text = e.target.value;
var newRootDiv = createView();
morphdom(origRootDiv, newRootDiv);
});
return rootDiv;
}
origRootDiv = createView();
document.body.appendChild(origRootDiv);
EDIT
The current behavior is that if you type 1 character in the field the text field retains focus (good), but if you type a second character in the field and the "Even char count!" message is shown the textfield loses focus (bad).
The desired behavior is that even if the message is shown, the textfield should retain focus.

Add .focus() to the input by using id.
see this https://www.w3schools.com/jsref/met_text_focus.asp
Solution
See the updated code,
input.addEventListener("input", function(e) {
text = e.target.value;
var newRootDiv = createView();
morphdom(origRootDiv, newRootDiv);
// added the input with Id here
document.getElementById('this-should-help-right').focus();
});
I have added document.getElementById('this-should-help-right').focus(); after morphdom call. So i think the problem is solved

Related

Simulate cut function with JavaScript?

I'm working on a chrome extension and I want to do something with the text that is selected (highlighted by the user) on the page. For that, I need a way to remove the selected text, for example text inside an input field.
I found a way to "clear" the selected text, meaning it will be unselected:
Clear Text Selection with JavaScript But it doesn't seem to be what I'm looking for.
This just removes the highlighting from the text:
window.getSelection().empty();
I want to remove the text that is selected, if it's editable text. Is this possible with JavaScript?
You can use the deleteFromDocument method:
window.getSelection().deleteFromDocument()
This will immediately remove the selected content from the document, and as such also clear the selection.
As described formally in the MDN web docs:
The deleteFromDocument() method of the Selection interface deletes the selected text from the document's DOM.
If you'd like to be able to delete text from input elements instead, you need to use different APIs:
var activeEl = document.activeElement;
var text = activeEl.value;
activeEl.value = text.slice(0, activeEl.selectionStart) + text.slice(activeEl.selectionEnd);
Edit from me, Synn Ko: to cover input fields, textareas and contenteditables, use this:
var selection = window.getSelection();
var actElem = document.activeElement;
var actTagName = actElem.tagName;
if(actTagName == "DIV") {
var isContentEditable = actElem.getAttribute("contenteditable"); // true or false
if(isContentEditable) {
selection.deleteFromDocument();
}
}
if (actTagName == "INPUT" || actTagName == "TEXTAREA") {
var actText = actElem.value;
actElem.value = actText.slice(0, actElem.selectionStart) + actText.slice(actElem.selectionEnd);
}

Javascript event listener behavior

I added a listener to an unordered list to perform a function when a text area element within the ul was changed. When the text area is changed, I wanted to get the value of the now changed text area, and save it. When I try to save the value in newNotes, I am given back the INITIAL value of the text area, not the value after the text area has been changed. Isn't that the whole point of the listener, to be triggered upon a change?
ul.addEventListener('change',(e)=> {
if(e.target.tagName === "TEXTAREA") { // if the ul was changed and a textarea was targeted
const li = e.target.parentNode; // the parent list item of the text area
const liName = li.firstChild.textContent; // this is a string
var newNotes = e.target.textContent; // PROBLEM : RETURNS WRONG VALUE
console.log(newNotes);
updateNotesTo(liName, newNotes); // regarding localStorage
}
});
You want the value from textarea
Change
var newNotes = e.target.textContent;
To
var newNotes = e.target.value;
You have to use the .value attribute.
var newNotes = e.target.value;
See also, Textarea.textcontent is not changing

How to show end of the text in input after programmatically adding text?

I want to programmatically add words in input and always see the end of the text. But the problem is that when I'm adding word (like input.value += 'word') when length of text is almost the same as length of input, text inside input doesn't move, so I can see only begining of the text and ending is become hidden.
I think if I could put cursor to the end of input it will help, but none of tricks with cursor not working in Chrome, such as
input.value = input.value
or
input.setSelectionRange(input.value.length, input.value.length);
Seems to work well enough:
let input = document.getElementById('auto');
let sentence = 'Courage is the magic that turns dreams into reality.';
let index = 0;
setTimeout(function typing() {
let letter = sentence.charAt(index++);
input.blur();
input.value += letter;
input.focus();
input.setSelectionRange(index, index);
if (index < sentence.length) {
let ms = Math.floor(75 + Math.random() * 150);
setTimeout(typing, ms);
}
}, 1000);
<input id="auto" type="text">
If the input does not have focus, then Chrome will show it "scrolled" all the way to the left, showing the beginning of your input value.
If the element has focus, then you can set the selection range to move the cursor:
window.onload = function(){
var input = document.getElementById('foobar');
input.value += ' bazbat';
input.focus();
input.setSelectionRange( input.value.length - 1 );
}
<input id="foobar" value="foobar" size="6">
But it would be a very confusing and frustrating UX if the cursor is moving around while the user is attempting to type a value.
Other options to consider:
Change the width of your input to match the value. make html text input field grow as I type?
Toggle between an input and a span (edit mode vs read mode). With a span, you'd have a lot more control over how text is displayed. Jquery: Toggle between input and text on click
Use an indicator of some sort to alert the user that one of their input values was modified. This would be useful even if they can see the entire value, as they might not notice that the value was updated.

Autotab contenteditable divs or adding function to input type: text

I have 3 contenteditable divs. The first and third divs have a function that simulates changing the character associated with a key event, typing out a pre-programmed string as the user types, and the second div is straightforward- what the user types appears in the div. This can be seen here: http://jsfiddle.net/vRXph/4/. I've included the code for the first div here for convenience:
var list1 = "Sing in me, Muse, and through me tell the story".replace(/\s/g,
"\xA0").split("")
function transformTypedCharacter1(charStr) {
var position = $("#verse1").text().length;
if (position >= list1.length) return '';
else return list1[position];
}
function insertTextAtCursor1(text) {
var sel, range, textNode;
if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0).cloneRange();
range.deleteContents();
textNode = document.createTextNode(text);
range.insertNode(textNode);
// Move caret to the end of the newly inserted text node
range.setStart(textNode, textNode.length);
range.setEnd(textNode, textNode.length);
sel.removeAllRanges();
sel.addRange(range);
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.pasteHTML(text);
}
}
$("#verse1").keypress(function(evt) {
if (evt.which) {
var charStr = String.fromCharCode(evt.which);
var transformedChar = transformTypedCharacter1(charStr);
if (transformedChar != charStr) {
insertTextAtCursor1(transformedChar);
evt.preventDefault();
return false;
}
}
});
I want to autotab between these divs. In the first div, the autotab function would be called after the final pre-programmed character is typed, and for the second div the autotab would be called after a certain number of characters (lets say 5 characters, just to keep it short as a test).
My first question (of 3): How do I autotab between contenteditable divs?
I have found a way to autotab between input type: text fields here: http://jsfiddle.net/Ukkmu/73/ but I cannot seem to apply this to my divs. I attempted, and failed, to do this here: http://jsfiddle.net/Ukkmu/76/.
function auTab (currentDiv, currentDivSize, currentDivLength, nextDiv) {
if(currentDivSize == currentDivLength){
$('#' + currentDiv).next().focus();
};
};
$('div[id^="verse"]').keyup(function() {
var thisDiv = $(this).attr('id');
var thisDivSize = $(this).attr('size');
var thisDivLength = $(this).val().length;
auTab(thisDiv, thisDivSize, thisDivLength);
});
As you can see, the system just ignores the autotab function. I created a size of "5" on my first div as a test. I don't know if this is possible, but I did it because I saw that the autotab function is dependent on size. My second question (of 3) is: Can I assign a size or maxlength attribute to a contenteditable div? If I can, and if the autotabbing relies on this attribute, then I would simply assign the size of the first div to be the number of characters in my pre-programmed string (and for the second div I would assign 5 characters as test, as I mentioned above).
An alternative would be to change my contenteditable divs to input type: text fields. I did this here: http://jsfiddle.net/Ukkmu/74/, but as you can see my original function that I described in the first paragraph of this question no longer works. I end up with a repeated character (the "S" from my pre-programmed string) before the first field, instead of my pre-programmed string in the first field. For this test I put the size of the field as 3, just as a test. The final version would be the size of the entire pre-programmed string. My 3rd question (of 3), if applicable: How can I apply the function that simulates changing the character associated with a key even to input type= text fields?
My application of this code is that this is an art project. As the user types, the output on the screen alternates between a classic text (in this case, Homer's The Odyssey), and what the user is actually typing.
Sorry if this is a very long post, but I wanted to include as much information as possible.
My solution, thanks to pckill, answers question 3. I used input type: text fields with the following function:
var list1 = "This is a test.".replace(/\s/g, "\xA0").split("")
function transformTypedChar1(charStr) {
var position = $("#verse1").val().length;
if (position >= list1.length) return '';
else return list1[position];
}

making text boxes visible

I have a drop down if i click it will retrieve values from db.If thre are 4 values that has to pass into text box and make it visible.If 5 values then 5 values has to get visible.There will be a count if 4 boxes count has to get into 5th box.if 5 values then count has to get int0 6th box.
How do i do it?
If the text boxes are in the markup and you've just hidden them (e.g., style="display: none"), you can show them again by setting their style.display property to "":
textBoxElement.style.display = "";
For example, here's a button click handler that looks for a text field to show and shows it; if there aren't any more to show, it hides the button:
var myForm = document.getElementById('myForm');
document.getElementById('btnShowField').onclick = function() {
var index, field, foundOne, foundMore;
foundOne = foundMore = false;
for (index = 0; index < myForm.elements.length; ++index) {
field = myForm.elements[index];
if (field.type === "text" && field.style.display === "none") {
if (!foundOne) {
// Found one, show it
field.style.display = "";
foundOne = true;
}
else {
// Found more, so we don't need to hide the button
foundMore = true;
break;
}
}
}
if (!foundMore) {
// No more hidden fields, hide the button
this.style.display = "none";
}
};
Live example
If you want to add more text boxes to a form at runtime when they aren't in the markup, you can easily do that:
var textBox = document.createElement('input');
textBox.type = "text";
textBox.name = "somename";
formElement.appendChild(textBox);
Live example
Usually the structure will be a bit more complex than that, but that's the general idea.
Off-topic: A lot of these things can be made dramatically easier by leveraging a JavaScript library like jQuery, Prototype, YUI, Closure, or any of several others. They'll smooth over browser differences and provide a lot of value-add functionality, so you can focus on what you're actually trying to do rather than browser quirks and such.

Categories

Resources