JavaScript: Is there a way to revert or undo .innerHTML.toLowerCase();? - javascript

Is there a way to "reverse" changing the innerHTML of an element to lowercase? Example:
HTML:
<div class="wrapper">
<p>Some text.</p>
</div>
JavaScript:
someText = document.querySelector(".wrapper").innerHTML.toLowerCase();
console.log(someText)
// Console Log Output: "<p>some text.</p>"
Is there then a way to reverse, remove or undo the toLowerCase();, in order to return the paragraph text back to its original case? Obviously I can't just change it to UpperCase, as it will change all the text to uppercase instead of original case.
someText = document.querySelector(".wrapper").innerHTML.toLowerCase();
console.log(someText);
// Console Log Output: "<p>some text.</p>"
someText = document.querySelector(".wrapper").innerHTML.toUpperCase();
console.log(someText);
// Console Log Output: "<p>SOME TEXT.</p>"
However, toLowerCase(); and toUpperCase; were the only options I could find, nothing like toOriginalCase();. I want the console log to return <p>Some text.</p> again after I revert the change.
I am trying to use this method in a search functionality.
Currently on my website, https://www.codeeverydamnday.com/, if you search, for example, "Javascript" from the home page, you'll be brought to a page of search results that do indeed match and highlight the search term, but that is because I have lowercased everything on the page (as you can see in the first post, "day twenty-four", where I have several variations of the word "Javascript", like "javaScript" and "Javascript", that are all showing lowercase).
That is because I changed the search term to lowercase, then changed all the text on the results page to lowercase before I started matching the search term to its instances on the page and highlighting them. Once matched and highlighted, I would like to revert the text back to its original case and return that to the UI.
Edit: I also tried adding and then removing a style.textTransform = "lowercase"; , but kept running into issues with that.

You can simply store the original HTML on the element.
// nothing stops you from creating your own property on the div#wrapper element
// which you can use to store the original HTML in
wrapper.originalHTML = wrapper.innerHTML;
<div id="wrapper">
<p>Some text.</p>
</div>
<button onclick="wrapper.innerHTML = wrapper.innerHTML.toLowerCase()">toLowerCase()</button>
<button onclick="wrapper.innerHTML = wrapper.originalHTML">and back</button>
That being said, your whole toLowerCase() approach for searching is not what you need to do.
Instead, use regular expressions to do case-insensitive matching.
See this simple example:
s.addEventListener('input', () => {
let regex = new RegExp(s.value, 'gi');
console.log(text.innerText.match(regex));
})
<div id="text">foobar <span>FooBAR <i>fooBar</i> Foo</span>Bar foObAR</div>
<input type="search" id="s" placeholder="type "foobar" here">

Different answer, because the original post (prior to a fairly heavy edit) didn't mention this was for effecting text highlighting, having fallen in the trap of being a classic XY question (asking about how to undo property assignment when the real problem was how to get to look lowercase).
The actual solution here is to not do text replacement at all, but to make sure that the highlighting markup is tied to CSS, not JS, that ensures that no matter what case the text actually is in, it's presented as lowercase to the user, with a highlight background color:
p {
font-family: Verdana;
}
.highlight {
text-transform: lowercase;
background: #FFFF0050;
}
<p>
For example, to highlight <span class="highlight">JavaScript</span> in
this "paragraph" of <span class="highlight">TEXT</span>.
</p>
The page text casing never changes, but the presentation does. We get "everything in lowercase" entirely for free without needing any textContent or innerHTML caching, or needing to "undo" anything with respect to the source. CSS only changes the way our source is presented, and the moment we take the highlight class (or outer tags) away, the effect goes away and we see the original text case again.

You can refer to the first letter of the text by converting that letter to toUpperCase():
someText.charAt(0).toUpperCase() + someText.substring(1)
The first console shows the result from toLowerCase(). And the second console simulates the primary state of the text based on toLowerCase() above.
let someText = document.querySelector(".wrapper p").innerHTML.toLowerCase();
console.log(someText);
console.log(someText.charAt(0).toUpperCase() + someText.substring(1));
<div class="wrapper">
<p>Some text.</p>
</div>

Short answer: no.
Longer answer: no, because innerHTML (and textContent, which you should be using instead for text, never use innerHTML unless you're actually building HTML, and even then, using the DOM methods is almost always better) are standard object properties on DOM nodes, and if you overwrite a property with a new value, that's its new value. There is nothing to restore, because the only thing that exists is the current value.
Of course, you can certainly reformat the string after the fact (e.g. capitalising every first letter in a sentence) but the original value is lost: this will be a new operation, based the exising lowercase string.

Related

JavaScript: Change All Text in Element to Lowercase (not just on UI but in DOM as well)

Trying to put together a somewhat hacky way of finding strings that match a word case-insensitively and returning their parent element in the console with my limited JavaScript knowledge.
Say you have this html - a search input box with id="searchinput" along with a Submit button with class="submitButton", and then some content below it.
<input type="text" placeholder="Search..." id="searchinput">
<button type="submit" class="submitButton">Submit</button>
<div class="wrapper">
<p>Charlie sat on the chair.</p>
</div>
<div class="wrapper">
<p>It's a nice day today.</p>
</div>
<div class="wrapper">
<p>charlie went to the store.</p>
</div>
And then you have this JS, an event listener that triggers when you click the "Submit" button, which loops through all the div's with class="wrapper" looking for any strings within the div that match the searchValue you've entered, and console logging each wrapper div that does contain the search term:
function submitButtonFunction() {
searchValue = document.getElementById('searchinput').value;
// variable that defines the word you type into the search box
allWrappers = document.querySelectorAll('.wrapper');
// variable that defines all of the elements with the class "wrapper"
allWrappers.forEach(
e => { if (e.innerHTML.includes(searchValue)) {
return console.log(e);
} else {
return;
}
});
// loops through each "wrapper" div's innerHTML looking for divs that contain your search word and console logging the div
document.querySelector('.submitButton').addEventListener('click', submitButtonFunction, false)
// calls the function on click of submit button
If you search the word "charlie", it would console log only the third <div> above, because it is the only one that exactly matches "charlie" in all lowercase. However, I want the first <div> to return too, the one with "Charlie" in uppercase.
I tried something like this, changing the searchValue to lowercase, then temporarily changing all the text in the wrapper div's to lowercase with the css text-tranform: lowercase property, before looping through the wrapper div's to find the ones that included the searchValue string, then removing the text-transform property afterward:
function submitButtonFunction() {
searchValue = document.getElementById('searchinput').value.toLowerCase();
// changes the word you searched to lowercase
allWrappers = document.querySelectorAll('.wrapper');
allWrappers.forEach(
e => { e.style.textTransform = "lowercase";
// changes all the text in the wrapper div to lowercase
if (e.innerHTML.includes(searchValue)) {
e.style.removeProperty('text-transform');
// after finding div's that include the search term, change back to regular case
return console.log(e);
} else {
e.style.removeProperty('text-transform');
return;
}
});
However, this only changed how the text appeared on the UI (lowercase), while remaining unchanged in the DOM (regular case), as seen in this screenshot below. Note: even when I removed the .style.removeProperty('text-transform'); line, the console log was still bringing back e in regular case. Though my code is slightly different in this image, you get the idea:
Since the DOM remains regular case, searching "charlie" still only brings back the lowercase instances of "charlie" in the console.
Is there a way to actually change the case in the DOM, do a find/compare, and bring back the elements that include a match, then change the case back?
I read other questions that allow you to do a comparison like this:
var areEqual = string1.toUpperCase() === string2.toUpperCase();
But in order to make that work for me, I would have to put every single word inside of a <span> or something in order to loop through all the spans and compare the values, whereas I am looking through a big section of words and trying to find any instances where the search value is included in that text.
I also read about another way you can do a comparison using RegExp, but I haven't learned RegExp yet and am having some trouble understanding the concept.
Of course, if there is an easier way to do this that I'm missing, would love to hear that as well! Keeping in mind I'm a JS newbie :)
When getting the value of the input, convert it into toLowerCase
searchValue = document.getElementById("searchinput").value.toLowerCase();
It would be great if getting the text inside the wrapper using textContent so that we can apply toLowerCase on it.
if (e.textContent.toLowerCase().includes(searchValue)) {
TIP: why to use return with console.log return console.log(e); . console.log returns undefined and better to use console.log(e)
const result = console.log(10);
console.log(result);
function submitButtonFunction() {
searchValue = document.getElementById("searchinput").value.toLowerCase();
// variable that defines the word you type into the search box
allWrappers = document.querySelectorAll(".wrapper");
// variable that defines all of the elements with the class "wrapper"
allWrappers.forEach((e) => {
if (e.textContent.toLowerCase().includes(searchValue)) {
return console.log(e);
} else {
return;
}
});
}
// loops through each "wrapper" div's innerHTML looking for divs that contain your search word and console logging the div
document
.querySelector(".submitButton")
.addEventListener("click", submitButtonFunction, false);
// calls the function on click of submit button
<input type="text" placeholder="Search..." id="searchinput">
<button type="submit" class="submitButton">Submit</button>
<div class="wrapper">
<p>Charlie sat on the chair.</p>
</div>
<div class="wrapper">
<p>It's a nice day today.</p>
</div>
<div class="wrapper">
<p>charlie went to the store.</p>
</div>

How to choose a specific word from a page by taking double-clicked and store it into a variable

For example:
HTML
<body>
<h1>Hello World</h1>
</body>
Javascript
function chooseAWordFromPage(e){
const words = e.target.innerText
//I get stuck here. Please help me
}
document.body.addEventListener('dblclick',chooseAWordFromPage)
I am trying to get one word inside the element by double click this word on page in brower. For example: "Hello World". How can I get "Hello" by clicking on page and assign it to a variable?
Any libraries are not allowed, such as jQuery.
try (after run snippet double click on "Hello World")
function chooseAWordFromPage(e){
const w = ''+window.getSelection();
console.log(w);
}
document.body.addEventListener('dblclick',chooseAWordFromPage)
<body>
<h1>Hello World</h1>
</body>
This sounds like an XY problem (you're asking how to implement your solution, rather than explaining your problem). If you want to get specifically the word that was double-clicked, you shouldn't be doing this with innerText; innerText just gives the full text and there's no way to determine which part of that text was clicked.
When you double-click a word on a page, it's normally selected, so what you really want to do here is get the selected text on the page, not try to parse it from the clicked element:
function chooseAWordFromPage(){
const word = window.getSelection().toString();
}
document.body.addEventListener('dblclick',chooseAWordFromPage)
Unless each word is surrounded by <span> tags, there's no way you could separate the words out into a discrete DOM node. Kamil's words.split(" ") solution would get you started, but there's nothing else for the click event to attach to.

Get text using jquery with text-transform

I have an html element and i toggle its class and show capital/small letters with text-transform.
Is it possible to get the text its text-transform?
$('#toggle').click(function(){
$('#char').toggleClass('upper');
});
$('#getdata').click(function(){
var text = $('#char').text();
alert(text); /// here i need to get the actual word with capital/lower i selected
});
.upper{
text-transform:uppercase;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span contenteditable="true" id="char">a</span>
<br/>
<button id="toggle">Toggle case</button>
<button id="getdata">gat data</button>
you can check for the class and use toUpperCase:-
$('#toggle').click(function(){
$('#char').toggleClass('upper');
});
$('#getdata').click(function(){
var $char = $('#char');
var text = $char.hasClass('upper') ? $char.text().toUpperCase() : $char.text();
alert(text); /// here i need to get the actual word with capital/lower i selected
});
.upper{
text-transform:uppercase;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span contenteditable="true" id="char">a</span>
<br/>
<button id="toggle">Toggle case</button>
<button id="getdata">gat data</button>
There is currently no way to get the rendered text with JavaScript.
When you are using English, toUpperCase and toLowerCase works well for the CSS value uppercase and lowercase.
But when you need it for non-English, or when you use capitalize, full-width etc., you have to reproduce the CSS logic (mostly unicode logic) with JS.
Below is a few rules that Firefox is doing. Chrome also knows some of them.
In German (de), the ß becomes SS in uppercase.
In Dutch (nl), the ij digraph becomes IJ, even with text-transform: capitalize, which only put the first letter of a word in uppercase.
In Greek (el), vowels lose their accent when the whole word is in uppercase (ά/Α), except for the disjunctive eta (ή/Ή). Also, diphthongs with an accent on the first vowel lose the accent and gain a diaeresis on the second vowel (άι/ΑΪ).
And so on...
It's also fun when you need to apply other CSS values:
capitalize - What constitutes a "word"? How do browsers split iPhone-6s+? Behold, Unicode consortium to the rescue!
full-width - The MDN example looks easy, but it does not show them all, for example [] to [], and maybe someday they will convert ... to … instead of ...
full-size-kana - How's your Japanese? No worry, this CSS4 proposals is dropped - in preference of a (future) fully customisable character mapping rules! Hope your CSS parser skill is up to par.
So, count yourself lucky if you use only English. You have my consolation if you, like me, work with multilingual systems. Timezone is nothing at all.
Maybe like this?
// Store css to javascript values in this object
var textTypes = {
"uppercase": "toUpperCase",
"lowercase": "toLowerCase"
}
// get the element
var div = document.getElementsByTagName('div')[0];
// get the computed style type
var type = window.getComputedStyle(div)['text-transform'];
// print the transformed text
console.log(div.innerHTML[textTypes[type]]());
Working Fiddle

Simulate paste into Textarea with javascript

When pasting data from the clipboard into an html textarea, it does some pretty cool parsing to make the paste look close to what was copied as far as newlines go.
For example, I have the following html on a page that I select (everything highlights in blue) and then I copy it:
Hello
<br>
<br>
<br>
<div>more</div>
<div>last</div>
So to be clear, what I am copying is what the output of this jsfiddle looks like.
Now when I paste this magical, copied text into a text area, I see exactly what I would expect: "Hello", empty line, empty line, empty line, "more", "last". Cool! Then when I use jQuery's .val() on the textarea, I get "Hello\n\n\nmore\nlast". Super cool. It took the br's and div's and was able to infer the correct newlines from it.
Now...
What I am trying to do it programmatically take the same data I copied earlier and set it as the textarea's value as if it were pasted.
Here is what I have tried...
So, say the stuff I copied earlier was wrapped in a <div id="parent">...</div>.
var parent = $("#parent");
var textarea = $("#theTextArea");
// Set the value of the text area to be the html of the thing I care about
textarea.val(parent.html());
Now I know this isn't the same as a copy-paste, but I was hoping it would take care of me and do what I wanted. It doesn't. The textarea gets filled with Hello<br><br><br><div>more</div><div>last</div>. The html that was once invisible is now stringified and made part of the text.
Obviously I did this wrong. .html() returns, of course, the string of html. But is there something I could call on parent that would give me the text with all inferred linebreaks?. I have tried calling parent.text(), but this only gives Hellomorelast (no line breaks).
A few notes that could help with an answer: I am using Angular, so I have access to all their goodies and jQuery.
Edit:
Solution
It is not nice but you can try to replace html tags with line breaks '\n' or do some line breaks in the html file and get the content with text().
var parent1 = $("#paren1");
var textarea1 = $("#theTextArea1");
var parent2 = $("#paren2");
var textarea2 = $("#theTextArea2");
// Set the value of the text area to be the html of the thing I care about
var text = parent1.html();
text = text.replace(new RegExp("<br>", 'g'),"\n");
text = text.replace(new RegExp("<div>", 'g'),"");
text = text.replace(new RegExp("</div>", 'g'),"\n");
textarea1.val(text);
textarea2.val(parent2.text());
JSFiddle

Merge Text Node after wrap and unwrap jquery

I have the following HTML code:
<p> hello every body, to day is a sunny day</p>
I have use wrap() method to wrap element with <em> tag like the following:
<p> hello every body, to day is <em>a sunny day<em></p>
When I finished my test, I use $("em").contents().unwrap() to unwrap all <em> tag:
I make a loop for all elements in my page
So I found that
-----hello every body, to day is a----
and
-----a sunny day-----
are 2 seperated text nodes,
How can I use wrap() and unwrap(), so my text won't be splited like that?
I'm afraid, you cannot use .wrap() and .unwrap() to do what I think you intend to do. .unwrap() calls replaceWith() in jQuery code which eventually calls .replaceChild(). The replaceChild() method in JS, replaces one DOM child node, with another. The use of .contents() is creating a text node.
This updates the render tree in the browser, which in turn updates the markup you see in a developer tool. The nodes in a render tree are placed on their own lines for the markup... not to be confused with what actually ends up being displayed: since \n is a whitespace character, the text nodes will show in one line on the screen.
If you need the actual text (innerHTML) to not be broken up as textNodes you will have to resort to somewhat drastic, measures:
$(document).ready(function () {
var _childContent = $('em').contents()[0].data,
_childIndex = $('p')[0].innerHTML.indexOf('<em>'),
_parentText;
$('em').remove();
_parentText = $('p')[0].innerHTML;
$('p')[0].innerHTML = _parentText.substr(0, _childIndex) +
_childContent +
_parentText.substr(_childIndex + 1, _parentText.length);
});
Due to having to take such measures, I would agree with grmmph's comment about if it is an issue of styling, then take the addClass() approach.
However, if what you want is to have all the text between the <p> and </p> tags appear on one line instead of being treated as textNodes, then the above works (at least in IE 10 when viewed in the developer tool).
This question goes back a while, so my answer may only be of use to people in the future facing similar problems to the OP (as I was myself).
A neat way to re-combine the unwrapped text nodes is by resetting the innerHtml of the span's parent element using $.html()
HTML
<p>Lorem ipsum <span>dolor sit</span> amet.</p>
JS
function reset () {
var parent = $('span').parent();
$('span').contents().unwrap();
parent.html(function(i, html) {
return html;
});
}
This has been asked so long ago, but because an answer hasn't been marked yet, I'll post an answer. I'm not sure if you wanted those text nodes split or not, but both ways can be done like this (run snippets to see it work):
The vanilla Javascript way (found here):
function unwrapElement () {
var el = document.querySelector('em')
var parent = el.parentNode
while (el.firstChild) parent.insertBefore(el.firstChild, el)
parent.removeChild(el)
}
<p>Hello every body, to day is <em>a sunny day<em></p>
<button onclick="unwrapElement()">Do unwrap</button>
The JQuery way (similar to #StevieP's way):
function unwrapElement() {
$('em').contents().unwrap()
$('em').parent().html((i, html) => html) // if you remove this, then the text will be split
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Hello every body, to day is <em>a sunny day<em></p>
<button onclick="unwrapElement()">Do unwrap</button>

Categories