Strike a String though JavaScript - javascript

In my if/else statement, instead of having:
todoTextWithCompletion = '(x) ' + todo.todoText;
I want to display a strike string (todo.todoText input) in the DOM. I've tried replacing it to
todoTextWithCompletion = todo.todoText.strike();
But it's displaying <strike>todo.todoText</strike> in the DOM.
displayTodos: function () {
var todosUl = document.querySelector('ul');
todosUl.innerHTML = '';
todoList.todos.forEach(function (todo, position) {
var todoLi = document.createElement('li');
var todoTextWithCompletion = '';
if (todo.completed === true) {
todoTextWithCompletion = '(x) ' + todo.todoText;
} else {
todoTextWithCompletion = '( ) ' + todo.todoText;
}
todoLi.id = position;
todoLi.textContent = todoTextWithCompletion;
todoLi.appendChild(this.createToggleButton());
todoLi.appendChild(this.createDeleteButton());
todosUl.appendChild(todoLi);
}, this);
},

You can also style your text content using JavaScript.
Assuming todoLi is the element you want to strike-through:
todoLi.style.textDecoration = "line-through";
Using the new CSS Typed Object Model, this will be:
todoLi.attributeStyleMap.set('text-decoration', 'line-through');
Both will have the same effect as if you're applying the text-decoration: line-through; property using CSS.
Also keep in mind that The <strike> tag is not supported in HTML5.
Use <del> or <s> instead.

You are setting textContent to an HTML string. textContent is not parsed as HTML, it is added as plain text:
Differences from innerHTML
Element.innerHTML returns HTML as its name indicates. Sometimes people
use innerHTML to retrieve or write text inside an element. textContent
has better performance because its value is not parsed as HTML.
Moreover, using textContent can prevent XSS attacks.
Instead, you should construct the <strike> node using document.createElement("strike");, use innerHTML, or as U-ways suggested, add the text-decoration: line-through CSS style.
document.getElementById("text-content").textContent = "<strike>foo</strike>"
document.getElementById("inner-html").innerHTML = "<strike>foo</strike>"
<div id="text-content"></div>
<div id="inner-html"></div>

Rather than adding a raw style to the element, you can add a class using the classList API:
CSS
.item-completed {
text-decoration: line-through;
}
Javascript
var todoLi = document.createElement('li');
if (todo.completed === true) {
todoLi.classList.add('item-completed');
}

Related

Javascript: How to check/compare if a String contains some tag?

I'd like to use pure JS to check if some String, the textareas .innerHTML = newContent below, contains some tag (h1in my case) at the beginning (=as first child). What would be the best way to do this?
Thanks!
function submitNewSectionContent(e) {
for (var i = 0; i < sections.length; i++)
let newHeading = document.getElementById('edit-title').value;
/* edit-title is text-input*/
let newContent = document.getElementById('edit-sectionText').innerHTML;
/* edit-sectionText is textarea */
if (newContent.indexOf('<h1>') > -1 && newContent.indexOf('<h1>') < 10) { /* <h1> is at beginning so replace with newHeading */
let toberemoved = newContent.match('<h1>.*<\/h1>');
newContent = newContent.replace(toberemoved[0], '').trim();
sections[i].innerHTML = '<h1>'+newHeading+'</h1>' + sections[i].innerHTML;
} elseĀ { /* newContent has no h1 as first child, so add h1 from newHeading */
sections[i].innerHTML = '<h1>'+newHeading+'</h1>' + newContent;
}
}
}
Problem with Regular expressions is they do not really work well with HTML. So Your best bet is to convert it to a DOM fragment and do the manipulations and convert it back. Only issue with this method really is you can lose formatting. There are libraries out there that can pretty print HTML.
function updateHeadline(txt) {
const ta = document.querySelector("textarea");
const data = ta.value; // read value, not innerHTML
const temp = document.createElement('div'); // temp div to hold html
temp.innerHTML = data; // set the html to the temp element
let firstChild = temp.firstElementChild // look at the dom
if (!firstChild || firstChild.tagName!=="H1") { // see if we have an h1
firstChild = document.createElement("h1") // if not create one
temp.prepend(firstChild) // add it to the front
}
firstChild.innerHTML = txt // set the new text of the h1
ta.value = temp.innerHTML // put the content back into the textarea
}
const btn = document.querySelector("button");
btn.addEventListener("click", function (e) {
e.preventDefault()
updateHeadline(document.querySelector("#text").value)
})
textarea {
width: 300px;
height: 200px;
}
<textarea>
<p>Some other text</p>
<p>Some more text</p>
</textarea>
<input value="foo" id="text"/>
<button>Set</button>
You could use regex like so, (updated based on comment)
if( /^\s*<h1>/gi.test(stringToTest) ) {
//logic here
}
It checks if the stringToTest begins with ^ tag
See here : https://regex101.com/r/vSo4sL/1
convert to dom, and parse dom
this portion of code makes it possible to treat a chain to retrieve titles placed in the H1 tag and (on the fly) treat the string of characters.
It's easily expandable for future processing : or tag processing or other ...!
commented code
<html>
<script>
var s="<H1>Hey Title</H1>\n Hello,\n other title <H1>Green!</H1>\n Ipsum dolore sit...";
console.log(s);
console.log("-------------------------");
var partialDoc = document.createElement( 'html' );
partialDoc.innerHTML = s;
var parsed='';
var titles=[];
treatment(partialDoc);
console.log("\n-------------------------\n");
console.log("parsed",parsed);
console.log("\n-------------------------\n");
console.log("s var contains "+titles.length+ " H1 tag");
console.log("titles "+titles);
function treatment(root) {
var child = root.firstChild;
while (child) {
// child.nodeName = H1 | H2 | P etc...
// child.nodeType = 1
// catch H1
if (child.nodeName=='H1') {
// append your title,
parsed+=" [H1 FOUND content= {"+child.innerText+"} H1])";
// or
// parsed+="<H1>"+child.innerText+"<H1>";
// add your own process here
// add this title in array
// or what you want...
titles.push(child.innerText);
// next part of document
child = child.nextSibling;
continue;
}
// capture other text than H1
if (child.nodeType==3) { // Node Type Text
parsed+=child.nodeValue;
}
if (child.nodeType==1) { // Node Type ELEMENT, : sub nodes...
treatment(child);
}
// continue the rest of doc
child = child.nextSibling;
}
}
</script>
</html>
One way you could do it is: Node.firstElementChild which will avoid giving child node as #text for white-spaces and Node.nodeName
let firstChild = document.getElementById('edit-sectionText').firstElementChild;
if(firstChild.nodeName === "H1"){
firstChild.innerHTML = "Replacement Value"
}
Note & Update: The earlier api that I had suggested Node.firstChild will not prevent white-spaces which gives #text node and comments as #comment node.
2nd Way: Node.children and picking the first child out of it should have a similar result to Node.firstElementChild.
let elem = document.getElementById('edit-sectionText');
if(elem){
let firstChild = elem.children[0];
}
Update based on comments: Using Dom Parser Interface
The interface allows to parse XML or HTML source from a string based on the mime type provided for its method parseFromString(string, mimeType)
It will give the top level #document node with parsed HTML from the string where if exists <h1> or <H1> at the beginning would be the first child of body and subsequently can be tested via tagName property.
Note: Takes care of preceding HTML comments and spaces at the beginning but a caveat is doesn't check fully closed tags ex: var s = \t <h1>I am a heading <h1> here the <h1> was never closed and in the result will two fully formed headings at the body with content : I am a heading and ""
let textAreaString = document.getElementById("edit-sectionText").value;
const domParser = new DOMParser();
const parsedDoc = domParser.parseFromString(textAreaString, "text/html");
if (parsedDoc.body.firstElementChild.tagName === "H1") {
//yes it starts with <h1> or <H1>
}

How do I modify a css element with Jquery after appended with Jquery to a div?

I am making a todo list so I am adding new input tags with checkboxes on them so I can check off a to do list item. I am having trouble modifying the css so that the text displays a line through it when it is checked.
The check function works and it runs but I am unable to put a line through the specific html element.
var add = function() {
var task = $("#taskTextBox").val(); // references the value inside #task
if(task != ""){
inc++;
var itemName = "item" + inc;
var newObj = $("#list").append("<input id="+itemName+" type=checkbox class="+itemName+">"+task+"<br></br>");// makes a new <p> element
$("#taskTextBox").val(""); // empties out #task
var newHeight = $("#list").height() + 40;
$("#list").css({"height": newHeight});
addAttributes();
$("input[class=" + itemName + "]").change(function () {
if ($(this).prop("checked")) {
$("input[class=" + itemName + "]").css({'text-decoration': 'line-through'});
} else{
$("#list").css({"text-decoration": "none"});
}
});
}};
EDIT:
I forgot to mention that your code is not working because you are adding your class to the input which is just affecting the checkbox style not the text following it.
For my code to work you just need to modify this line, adding a span to wrap your task. You do not need the change function anymore.
var newObj = $("#list").append("<input id="+itemName+" type=checkbox class="+itemName+"><span>"+task+"</span><br></br>");// makes a new <p> element
SOLUTION:
You can achieve this with plain CSS using :checked pseudo-class and using a span or any other tag that can be used as a selector directly following your input:
JSFiddle
CODE SNIPPET:
.example:checked + span {
text-decoration: line-through;
}
<input class="example" type="checkbox"><span>Eggs</span>
<input class="example" type="checkbox"><span>Milk</span>

JavaScript get textContent excluding children

First, I'm creating a library for JavaScript and I can not use jQuery. I'm trying to get the text content of an HTML element without the text contents of its children.
Both attributes innerText and textContent don't give me what needed, please help.
You can solve using DOM API as childNodes and nodeType.
var elChildNode = document.querySelector("#hello").childNodes;
var sChildTextSum = "";
elChildNode.forEach(function(value){
if(value.nodeType === Node.TEXT_NODE) {
console.log("Current textNode value is : ", value.nodeValue.trim());
sChildTextSum += value.nodeValue;
}
});
console.log("All text value of firstChild : ", sChildTextSum);
I created a sample code as above.
https://jsfiddle.net/nigayo/p7t9bdc3/
To get Author's Name from the following element, excluding <span>...:
<div class="details__instructor">
Author's Name<span ng-show="job_title">, Entrepreneur</span>
</div>
use childNodes[0]. For example:
document.querySelector('div.details__instructor').childNodes[0].textContent
Using only JavaScript (you specified you cannot use jQuery), and given that you have provided and know the id for the parent element:
document.getElementById('parent_element_id').childNodes[0].nodeValue;
You can also use .trim() to remove any trailing space characters left behind from the removal of any child element text:
document.getElementById('parent_element_id').childNodes[0].nodeValue.trim();
var mydiv = getElementByID("id");
function Get_text(element) {
var selected = element.cloneNode(true);
var text;
while (selected.firstChild) {
if (selected.firstChild.nodeType == 3) text = selected.firstChild.nodeValue;
selected.removeChild(selected.firstChild);
}
return text;
}
Get_text(mydiv);
I know many good solutions here exist, but none of them actually achieved what I needed (get the textContent of a single node, none of its children), so sharing this for future searchers.
var html = document.getElementsByTagName("*");
for (var i = 0; i < html.length; i++) {
var el = html[i];
for (var j = 0; j < el.children.length; j++) {
var child = el.children[j],
childTextContent = child.innerHTML;
// Remove all children tags, leaving only the actual text of the node.
childTextContent = childTextContent.replace(/\<.*\>.*\<\/.*\>/gmi, "");
// Also remove <img /> type tags.
childTextContent = childTextContent.replace(/\<.*\ \/\>/gmi, "");
console.log(childTextContent);
// Now you can do any type of text matching (regex) on the result.
}
});

extract div as HTML including CSS classes

I am trying to create a general function that will extract a div content (with nested elements) and save it locally in an HTML file.
Basically I get the div innerHTML, wrap it in html/head/body tags and then save it:
function div2html() {
var inner=document.getElementById("div2save").innerHTML;
var html="<html><head></head><body>"+inner+"</body></html>";
saveTextAsFile("div2html.html", html);
}
See a working version here: jsfiddle
However I am not sure how to handle classes. As you can see the class in the sample (bigbold) is not embedded in the new HTML. I need some way to get all the classes used in the div and then add them (or the computed styles ?) to the html I generate .. is this possible ? is there any other way around it ?
Try including style element .outerHTML within saved html
function div2html() {
var inner=document.getElementById("div2save").innerHTML;
var style = document.getElementsByTagName("style")[0].outerHTML;
var html="<html><head>"+style+"</head><body>"+inner+"</body></html>";
saveTextAsFile("div2html.html", html);
}
jsfiddle http://jsfiddle.net/fb6s763w/1/
Alternatively, using window.getComputedStyle() to select only css of #div2save child node
function div2html() {
var inner = document.getElementById("div2save");
var style = window.getComputedStyle(inner.children[0]).cssText;
var html = "<html><head><style>"
+ "." + inner.children[0].className
+ "{" + style + "}"
+ "</style></head><body>"
+ inner.innerHTML + "</body></html>";
saveTextAsFile("div2html.html", html);
}
jsfiddle http://jsfiddle.net/fb6s763w/2/
Looks like this might be able to help you out:
https://github.com/Automattic/juice
If the CSS of the page is not big, a simple solution is to include it all in the saved html as suggested by guest271314 above with
var style = document.getElementsByTagName("style")[0].outerHTML;
see jsfiddle
A more comprehensive solution extracts the classes from the div and then adds only the rules of those classes to the div (Using code from How do you read CSS rule values with JavaScript?)
function div2html(divId) {
var html = document.getElementById(divId).innerHTML;
// get all css classes in html
var cssClasses = [];
var classRegexp = /class=['"](.*?)['"]/g;
var m;
while ((m = classRegexp.exec(html))) cssClasses = cssClasses.concat(cssClasses, m[1].split(" "));
// filter non unique or empty cssClasses
cssClasses = cssClasses.filter(function (item, pos, self) {
return item && self.indexOf(item) == pos;
});
// get html of classes
var cssHtml = '';
for (var i = 0; i < cssClasses.length; i++) cssHtml += getRule('.' + cssClasses[i]);
// assemble html
var html = "<html><head><style>" + cssHtml + "</style></head><body>" + html + "</body></html>";
console.log(html);
saveTextAsFile("div2html.html", html);
}
see jsfiddle

Replace specific text without affecting styling

Need to replace colons wherever they're found in specific elements with id's. This works but for some reason also replaces the styling attached to the elements:
$(document).ready(function(){
$("#element").each(function () { //for element
var s=$(this).text(); //get text
$(this).text(s.replace(/:/g, ' ')); //set text to the replaced version
});
});
I tried attaching a class to the element but it made no difference. How do I just remove the colons without affecting anything else?
Demo
I think you want to use the .html() method: http://api.jquery.com/html/
because according to this: http://api.jquery.com/text/
it calls the DOM method .createTextNode(), which replaces special characters with their HTML entity equivalents (such as < for <)
$(document).ready(function(){
$("#element").each(function () { //for element
var s=$(this).html(); //get text
$(this).html(s.replace(/:/g, ' ')); //set text to the replaced version
});
});
What is the HTML code in #element ?
If replacing the text in #element changes the style, it means that #element also contains some HTML tags (and the style is actually applied on those tags).
Try this:
$(document).ready(function() {
$("#element").each(function () {
(function trim_colons(el) {
for (var i = 0; i < el.childNodes.length; ++i) {
var c = el.childNodes[i];
if (c.nodeType == 1) trim_colons(c);
else if (c.nodeType == 3) c.nodeValue = c.nodeValue.replace(/:/g, ' ');
}
})(this);
});
});
This will correctly remove : characters, without changing the style.
Certainly you did'nt meant to have text in input types replaced ha!
For html elements other than input elements to fetch innerhtml you should use
var s=$(this).html();

Categories