Merge Text Node after wrap and unwrap jquery - javascript

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>

Related

How can I remove an <h2> dynamically?

Maybe this is a basic question, but I should stress I know very little about these things. Essentially, in my page I have something like:
<h2>Some text there</h2>
<h2>Other text there</h2>
And I would like to make it simply into:
Some text there
Other text here
I mean, essentially, I want to remove the H2 surrounding the text. It'd be super easy to just delete it, but unfortunately I only have indirect access to the code. Is there any way to remove this dynamically using Javascript?
You can loop through all h2 elements and replace each element with a text node of the element's textContent (with document.createTextNode):
document.querySelectorAll('h2').forEach(e => e.parentNode.replaceChild(document.createTextNode(e.textContent), e))
<h2>Some text there</h2>
<br/>
<h2>Other text there</h2>
Eventually, I found this solution, which was perfect for my case, but I'd also want to thank everyone who posted here!
document.addEventListener('DOMContentLoaded', e => {
const h2s = document.querySelectorAll("h2")
h2s[0].outerHTML = h2s[0].innerHTML
h2s[1].outerHTML = h2s[1].innerHTML
})

HTML DOM manipulation : properly replace tag by heading tag

I want to replace some tag-inside-a-paragraph-tag by a heading-tag-enclosed-by-a-paragraph tag. This would result in proper W3C coding, but it seems that jQuery is not able to manipulate the DOM in the right way!? I tried several ways of (jQuery) coding, but i can't get it to work ..
Original code:
<p>some text <span>replace me</span> some more text</p>
Desired code:
<p>some text</p><h2>my heading</h2><p>some more text</p>
Resulting code by jQuery replaceWith():
<p>some text<p></p><h2>my heading</h2><p></p>some more text</p>
Demo: http://jsfiddle.net/foleox/J43rN/4/
In this demo, look at "make H2 custom" : i expect this to work (it's a logical replace statement), but it results in adding two empty p-tags .. The other 2 functions ("make code" and "make H2 pure") are for reference.
Officially the W3C definition states that any heading tag should not be inside a paragraph tag - you can check this by doing a W3C validation. So, why does jQuery add empty paragraph tags? Does anybody know a way to achieve this? Am i mistaken somehow?
You can achieve this with this code. However it's pretty ugly:
$('.replaceMe').each(function() {
var $parent = $(this).parent(),
$h2 = $(this).before('$sep$').wrap('<h2>').parent().insertAfter($parent);
var split = $parent.html().split('$sep$');
$parent.before('<p>' + split[0] + '</p>');
$h2.after('<p>' + split[1] + '</p>');
$parent.remove();
});
http://jsfiddle.net/J43rN/5/
If you read the jQuery docs, you will find:
When the parameter has a single tag (with optional closing tag or
quick-closing) — $("<img />") or $("<img>"), $("<a></a>") or $("<a>")
— jQuery creates the element using the native JavaScript
createElement() function.
So that is exactly what it is doing. And as I said in my comment, you can't change a parent node from a child node, you're altering the DOM here, not HTML code. So you'll need to either use replaceWith on the parent node and replace everything or use something like remove and append to split it up in multiple elements which you append after each other.
Try this:
var temp = "<p>some text <span>replace me</span> some more text</p>";
temp.replace(/(\<span\>replace me\<\/span\>)/gi, '</p><h2>my heading</h2><p>');
This will do a case insensitive replace for multiple occurences as well.
Read more about capturing groups here
Original credit to this question!
Please try this I have updated the http://jsfiddle.net/J43rN/6/ example by the below java script function please check I hope it will work for you
function fnMakeCode() {
$('#myP #replaceMe').html("<code id='replaceMe'>My Code</code>");
}
function fnMakeH2pure() {
$('#myP #replaceMe').html("<h2 id='replaceMe'>My H2 pure</h2>");
}
function fnMakeH2custom() {
$('#replaceMe').html("<p></p>").html("<h2>My H2 custom</h2>");
}

Change background color of arbitrary text with javascript

Is there an easy way to wrap spans around arbitrary text within an html paragraph? For example, given the following original html:
<p>Here is a dandy block of text to color up</p>
<p> WHOAH another paragraph</p>
I'd like to wrap arbitrary portions of the text based on user input. So one set of input might transform this into
<p>Here is a <span style="background:yellow">dandy block</span> of text to color up</p>
<p> WHOAH <span style="background:green">another paragraph</span></p>
While another set of input might create
<p>Here is a<span style="background:yellow">a dandy block</span> of text to color up</p>
<p> WHOAH <span style="background:green">another</span> paragraph</p>
This problem is related to this one and this one, however, the main difference with my goal is that I want the highlights to be permanent, not just temporary selections and I'd also like this to work within p elements rather than textareas.
If it's possible, I imagine it would look something like using jQuery
var innerText = $('p')[p_index].slice(char_start, char_end).text();
$('p')[p_index].slice(char_start, char_end).html(
"<span style=\"background:yellow\">"+
innerText +
"</span>");
This would (in theory) select the p_index paragraph, grab the range between the given indices and replace it with a newly created span which has the original text nested inside of it. This clearly doesn't work since subscripting on the jQuery object does not return another inner jQuery object. Though
$("p").slice(0, 1).html("<span style=\"background: blue\">" +
$("p").slice(0, 1).text() +
"</span>");
Does exactly what I want on a paragraph level, but not on the within text level. I could use this approach to do the replacement by totally writing each paragraph given the character ranges I have, but if there's an easy way, I'd greatly appreciate suggestions.
$("p")[p_index]
gives you the actual DOM element that is that paragraph at p_index, so to get the contents of the paragraph you'd need to use:
$("p")[p_index].innerHTML
// OR
$("p")[p_index].textContent
Using jQuery would be easier though. You wouldn't use the jQuery slice() method to reduce the range to a single element, you'd use the .eq() method. Try something like this:
$('p').eq(p_index).html(function(i,currentText) {
return currentText.substring(0, char_start) +
"<span style=\"background:yellow\">" +
currentText.substring(char_start, char_end) +
"</span>" +
currentText.substring(char_end);
});
When you pass a function to the .html() method, jQuery sets the html to whatever you return from the function. jQuery passes the function the current (inner) html of the element so you can process it. (If you do this on a jQuery object containing more than one element your function is called once for each element so they can be processed individually.)
Demo: http://jsfiddle.net/62HHk/
I've used this plugin in the past with nice results.
Try this:
$('input[type=text]').keyup(function() {
var val = $.trim(this.value);
var text = $('p').text().split(' ')
$.each(text, function(i, v) {
if (v == val) {
text[i] = '<span>'+v+'</span>';
}
})
$('p').html(text.join(' '))
})
Fiddle
This should work. It can easily be turned into a function that takes the word you're looking for as a parameter.
jQuery.textReplace by Ben Alman
$('.text').replaceText( /hello/g, '<span classs="interesting">hello</span>' );

JS - Remove a tag without deleting content

I am wondering if it is possible to remove a tag but leave the content in tact? For example, is it possible to remove the SPAN tag but leave SPAN's content there?
<p>The weather is sure <span>sunny</span> today</p> //original
<p>The weather is sure sunny today</p> //turn it into this
I have tried using this method of using replaceWith(), but it it turned the HTML into
<p>
"The weather is sure "
"sunny"
" today"
</p>
EDIT : After testing all of your answers, I realized that my code is at fault. The reason why I keep getting three split text nodes is due to the insertion of the SPAN tag. I'll create another question to try to fix my problem.
<p>The weather is sure <span>sunny</span> today</p>;
var span=document.getElementsByTagName('span')[0]; // get the span
var pa=span.parentNode;
while(span.firstChild) pa.insertBefore(span.firstChild, span);
pa.removeChild(span);
jQuery has easier ways:
var spans = $('span');
spans.contents().unwrap();
With different selector methods, it is possible to remove deeply nested spans or just direct children spans of an element.
There are several ways to do it. Jquery is the most easy way:
//grab and store inner span html
var content = $('p span').html;
//"Re"set inner p html
$('p').html(content);
Javascript can do the same using element.replace. (I don't remember the regex to do the replace in one stroke, but this is the easy way)
paragraphElement.replace("<span>", "");
paragraphElement.replace("</span>", "");
It's just three text nodes instead of one. It doesn't make a visible difference does it?
If it's a problem, use the DOM normalize method to combine them:
$(...)[0].normalize();
$(function(){
var newLbl=$("p").clone().find("span").remove().end().html();
alert(newLbl);
});​
Example : http://jsfiddle.net/7gWdM/6/
If you're not looking for a jQuery solution, here something that's a little more lightweight and focused on your scenario.
I created a function called getText() and I used it recursively. In short, you can get the child nodes of your p element and retrieve all the text nodes within that p node.
Just about everything in the DOM is a node of some sort. Looking up at the following links I found that text nodes have a numerical nodeType value of 3, and when you identify where your text nodes are, you get their nodeValueand return it to be concatenated to the entire, non-text-node-free value.
https://developer.mozilla.org/en/nodeType
https://developer.mozilla.org/En/DOM/Node.nodeValue
var para = document.getElementById('p1') // get your paragraphe
var texttext = getText(para); // pass the paragraph to the function
para.innerHTML = texttext // set the paragraph with the new text
function getText(pNode) {
if (pNode.nodeType == 3) return pNode.nodeValue;
var pNodes = pNode.childNodes // get the child nodes of the passed element
var nLen = pNodes.length // count how many there are
var text = "";
for (var idx=0; idx < nLen; idx++) { // loop through the child nodes
if (pNodes[idx].nodeType != 3 ) { // if the child not isn't a text node
text += getText(pNodes[idx]); // pass it to the function again and
// concatenate it's value to your text string
} else {
text += pNodes[idx].nodeValue // otherwise concatenate the value of the text
// to the entire text
}
}
return text
}
I haven't tested this for all scenarios, but it will do for what you're doing at the moment. It's a little more complex than a replace string since you're looking for the text node and not hardcoding to remove specific tags.
Good Luck.
If someone is still looking for that, the complete solution that has worked for me is:
Assuming we have:
<p>hello this is the <span class="highlight">text to unwrap</span></p>
the js is:
// get the parent
var parentElem = $(".highlight").parent();
// replacing with the same contents
$(".highlight").replaceWith(
function() {
return $(this).contents();
}
);
// normalize parent to strip extra text nodes
parentElem.each(function(element,index){
$(this)[0].normalize();
});
If it’s the only child span inside the parent, you could do something like this:
HTML:
<p class="parent">The weather is sure <span>sunny</span> today</p>;
JavaScript:
parent = document.querySelector('.parent');
parent.innerHTML = parent.innerText;
So just replace the HTML of the element with its text.
You can remove the span element and keep the HTML content or internal text intact. With jQuery’s unwrap() method.
<html>
<head>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("button").click(function(){
$("p").find("span").contents().unwrap();
});
});
</script>
</head>
<body>
<p>The weather is sure <span style="background-color:blue">sunny</span> today</p>
<button type="button">Remove span</button>
</body>
</html>
You can see an example here: How to remove a tag without deleting its content with jQuery

Changing HTML depending on where I click with javascript/JQuery

In an html page you might have some code with two paragraphs like this:
<hr />
<p>The first paragraph</p>
<p>The second paragraph</p>
<hr />
and very simply, these two tags would render like this:
The first paragraph
The second paragraph
What I am interested in is to allow the user to click somewhere in the rendered html code so as to insert a new element with JQuery. For example if I click between the letter i and the letter r (just a click, no higlighting/selection)in the word f*ir*st found in the first paragraph, I would be able to insert a custom span element or whatever I like exactly in that position in the HTML code resulting in something like this:
<hr />
<p>The fi<span id="myCustomSpan"></span>rst paragraph</p>
<p>The second paragraph</p>
<hr />
Any ideas that can help me? My request excludes absolute positioning. That would not solve my issues.
This is dirty but makes use of contenteditable: http://jsfiddle.net/Jj9Mp/.
$('div').click(function(e) {
$('div').attr('contenteditable', true);
document.execCommand("InsertHTML", false, "<span class=red>!</span>")
e.stopPropagation();
});
$(':not(div)').click(function(e) {
if($(this).parents('div').length === 0) {
$('div').attr('contenteditable', false);
} else {
$('div').click();
}
e.stopPropagation();
});
Here is my solution : http://jsfiddle.net/gion_13/L6aeT/
It works by calculating the actual size (in px) of the string.
This can be done with somewhat complex scripting.
i can state an algorithm that you can try.
1.JQuery have an API offset() http://api.jquery.com/offset/ using that you can get the offset and element.
2.Now you need to take the innerHTML of the element, take it as string, from the offset Y val, split at position Y in the string. make the element as two elements.
3.Now you can directly create an Element in script using creteElement(tagname) and set the innerHTML and then insert it in between the two elements

Categories