Why won't this script append a child div element? - javascript

When I click on the p element with an onclick attribute calling the make_child function I would expect it to append a div element when ever it is clicked but it seams to be only appending a text node to the paragraph element what is the cause of this?
<script type="text/javascript">
function make_child(text, id, type) {
var text = document.createTextNode(text);
var target = document.getElementById(id);
var add = document.createElement(type);
var addtext = add.appendChild(text);
target.appendChild(addtext);
}
</script>
<p id="changeme" onclick="make_child('I have changed', 'changeme', 'div')">Click me to change</p>

change the last line to this
target.appendChild(add);
now you are appending to the correct element

Try doing target.appendChild(add) instead of target.appendChild(addtext)
Edit (more detail):
The syntax for appendChild (from MDN) is:
var child = element.appendChild(child);
Where child is the element being appended. In this case, addtext = add.appendChild(text) is set to text rather than add. Just doing target.appendChild(add) should solve this problem.
This also means the variable addtext is useless; you can remove it leaving only
add.appendChild(text)
for that line.

Related

Why <p> tag acts as a block element and inline element interchangeably

In the following code snippet using JavaScript, a button element is created and added to the dom as a child of a p tag. However, while the p tag is a block element, for some reason, the button element is displayed alongside the p tag's contents. Why doesn't it go to the next line? Here is the code I am currently using:
const generateNoteDOM = function(note) {
const noteEl = document.createElement('p');
const button = document.createElement('button');
button.textContent = 'x';
if (note.title.length > 0) {
noteEl.textContent = note.title;
} else {
noteEl.textContent = 'Unnamed note';
}
noteEl.appendChild(button);
return noteEl;
};
This is how it appears.
A p element is by default a block level element. What you think p is not p but its text node, bacause you just did that by setting textContent. The button is by default inline-block and therefore places itself right next to the text node. The bounding client rectangle of the p is therefore the textNode + button itself.
The reason that this happens is that you've made your button... called button a child of your p element noteE1, and as such it appears "inline" while it is simply the result of nesting the elements.
You can fix this by adding a wrapper element as follows.
const generateNoteDOM = function(note) {
const wrapper = document.createElement('div'); // All the content will go inside this element.
const noteEl = document.createElement('p');
const button = document.createElement('button');
button.textContent = 'x';
if (note.title.length > 0) {
noteEl.textContent = note.title;
} else {
noteEl.textContent = 'Unnamed note';
}
wrapper.appendChild(p)
wrapper.appendChild(button);
return wrapper
};
This will result in a dom looking like this:
<div>
<p>Unnamed note</p>
<button>x</button>
</div>
The reason it works this way is a basic concept in HTML; children elements generally are placed inside their parents. The p tag is still inline, but it won't force the button onto a new line because it's meant to be contained inside it. If you add a third element into the wrapper, you'll see that it won't go on the same line as the p tag. There are many other resources on this, and a multitude of exceptions to everything I've said, but this contains the basics.
Its because the <button> element is inside the <p> element:
<p>some text <button></button></p>
But the <p> is actually a block element:
<p>some text</p><button></button>
It being block-level doesn't change the behavior of its content. Naturally, inline items within the p are still inline.
Also, post HTML. The JavaScript doesn't have anything to do with the flow.

jQuery: How to select text between two closed html tags

I am trying to wrap the intro/help text in html document using jQuery.It is not inside any tag but between two closed html tags.
Please see attached code snippet for example. the 2nd end tag can also be other than <p>.
var txtHelp = jQuery('b.page-title').nextUntil('p').text();
console.log(txtHelp);
//jQuery('b.page-title').nextUntil('p').text().wrap("<p />");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b class="page-title"><h4>System Log</h4><hr class="text-primary"></b>
How to select this text and wrap it in new P-Tag
<p align="center">This can by any html tag</p>
The nextUntil() method not selects textnodes.
You can get the text node by nextSibling property of node and get text content by textContent property of text node.
var txtHelp = jQuery('b.page-title')[0] // get the dom object
.nextSibling // get the text node next to it
.textContent; // get text content
console.log(txtHelp);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b class="page-title"><h4>System Log</h4><hr class="text-primary"></b>
How to select this text and wrap it in new P-Tag
<p align="center">This can by any html tag</p>
UPDATE 1 : If you want to wrap the element by a p tag then do it like.
$( // wrap by jquery to convert to jQuery object
$('b.page-title')[0] // get the dom element also you can use `get(0)`
.nextSibling // get the textnode which is next to it
).wrap('<p/>'); // wrap the element by p tag
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b class="page-title"><h4>System Log</h4><hr class="text-primary"></b>
How to select this text and wrap it in new P-Tag
<p align="center">This can by any html tag</p>
UPDATE 2 : If it contains br tag and you want to include it as a text then do something tricky using contents() method.
var get = false;
$($('b.page-title')
.parent() // get it's parent
.contents() // get all children node including text node
.filter(function() {
if ($(this).is('b.page-title')) {
get = true; // if element is 'b.page-title' then set flag true , we need to get element from here
return false // return false that we don't need the 'b.page-title'
}
if ($(this).is('p')) // check element is `p`, that we need to get element uptop tag
get = false; // update flag
return get; // return the flag for filtering
})).wrapAll('<p/>'); // use wrapAll to wrap all elements withing single tag
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<b class="page-title"><h4>System Log</h4><hr class="text-primary"></b>
How to select this text
<br/>and wrap it in new P-Tag
<p align="center">This can by any html tag</p>
For a pure jQuery approach, you can try this:
var contents = $('b.page-title').contents(),
textNodes = contents.filter(function() { return this.nodeType === 3; });
console.log(textNodes[0].textContent);
See contents()

How to wrap the content of DOM Element into another Element with minimal performance overhead?

I would like to wrap the live content of a DOM element into another, keeping all the structure and all attached event listeners unchanged.
For example, I want this
<div id="original">
Some text <i class="icon></i>
</div>
to become
<div id="original">
<div id="wrapper">
Some text <i class="icon></i>
</div>
</div>
Preferably without jQuery.
If there is nothing else other than ID to distinguish your nodes, and given that #original has multiple child nodes, it would probably be simpler to create a new parent node and insert that:
var original = document.getElementById('original');
var parent = original.parentNode;
var wrapper = document.createElement('DIV');
parent.replaceChild(wrapper, original);
wrapper.appendChild(original);
and then move the IDs to the right place:
wrapper.id = original.id;
original.id = 'wrapper';
noting of course, that the variables original and wrapper now point at the 'wrong' elements.
EDIT oh, you wanted to leave the listeners attached... Technically, they still are, but they're now attached to the inner element, not the outer one.
EDIT 2 revised answer, leaving the event listeners attached to the original element (that's now the outer div):
var original = document.getElementById('original');
var wrapper = document.createElement('DIV');
wrapper.id = 'wrapper';
while (original.firstChild) {
wrapper.appendChild(original.firstChild);
}
original.appendChild(wrapper);
This works simply by successively moving each child node out of the original div into the new parent, and then moving that new parent back where the children were originally.
The disadvantage over the previous version of this answer is that you have to iterate over all of the children individually.
See https://jsfiddle.net/alnitak/d0jss2yu/ for demo
Alternatively, do it this way. It also displays result in the adjacent result div.
<div id="original">
Some text <i class="icon"></i>
</div>
<button onclick="myFunction()">do it</button>
<p type="text" id="result"></p>
<script>
function myFunction() {
var org = document.getElementById("original");
var i = org.innerHTML; //get i tag content
var wrap = document.createElement("div"); //create div
wrap.id="wrapper"; //set wrapper's id
wrap.innerHTML= i //set it to i tag's content
org.innerHTML=""; // clear #orignal first
org.appendChild(wrap); //append #wrapper and it's content
var result = org.outerHTML;
document.getElementById("result").innerText = result;
}
</script>
Updated answer:
This should work better and with less code than my previous answer.
var content = document.getElementById("myList").innerHTML;
document.getElementById("myList").innerHTML = "<div id='wrapper'></div>"
document.getElementById("wrapper").innerHTML = content;
EDIT: This will destroy any event listener attached to the child nodes.
Previous answer:
I don't tried it, but something like this should work:
var wrapper = document.createElement("DIV");
wrapper.id = "wrapper";
var content = document.getElementById("myList").childNodes;
document.getElementById("myList").appendChild(wrapper);
document.getElementById("wrapper").appendChild(content);
Create the wrapper element.
Get myList contents.
Add the wrapper element to myList.
Add myList contents to be child of the wrapper element.

How to get reference to the new DOM object after changing content with outerHTML?

I have a division that I need to change its outer HTML upon an event. The problem is that upon setting the outerHTML I am not able to reference the new selected DOM object unless I explicitly catch it again.
Is there a way to directly update the variable reference upon calling outerHTML (in my case the reference of the div variable below) ?
$("#changeDiv").click(function(){
var div = $(this).prev();
div[0].outerHTML = `<div id="imSecondtDiv"> <p> World </p> </div>`;
console.log(div); // logs [div#imFirstDiv, prevObject: n.fn.init[1], context: button#changeDiv]
// the following line does not affect the newly added division
// since the var `div` references the old DOM object
// unless I add div = $(this).prev(); before setting the html of
// the paragraph it will not set it
div.find('p').html('Override');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="imFirstDiv"> <p> Hello </p> </div>
<button id="changeDiv" >Change Div 1</button>
I have solved this by getting a reference element (sibling or parent) of tag that's going to be replaced.
Here is a function which is not dependent on which element are you going to change:
function replaceElement(ele, outerHTML)
{
var parent = false, refEle;
//if element that's going to be changed has previousElementSibling, take it as reference. If not, the parentElement will be the reference.
if (ele.previousElementSibling !== null)
refEle = ele.previousElementSibling;
else
{
refEle = ele.parentElement;
//indicate that parentElement has been taken as reference
parent = true;
}
//change the outerHTML
ele.outerHTML = outerHTML;
//return the correct reference
if (parent)
return refEle.firstElementChild;
else return refEle.nextElementSibling;
}
So in your case, you would invoke it this way:
div[0] = replaceElement(div[0], '<div id="imSecondtDiv"> <p> World </p> </div>');
I hope it will work with jQuery as well, as I am writing all my scripts only in native javascript.
As you are seeing changing the outerHTML makes things behave a bit strangely, as you are completely replacing the original element but still referencing the old one.
It would be better to create a new div, add it after() the old one then remove() the old one. This maintains the position of the div in the correct place.
$("#changeDiv").click(function(){
// get the oldDiv
var oldDiv = $(this).prev();
// Create a newDiv
var newDiv = $('<div id="imSecondtDiv"> <p> World </p> </div>');
// add newDiv after oldDiv one, then remove oldDiv from the DOM.
oldDiv.after(newDiv).remove();
// now you still have the reference to newDiv, so do what you want with it
newDiv.find('p').html('Override');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="imFirstDiv"> <p> Hello </p> </div>
<button id="changeDiv" >Change Div 1</button>
Using outerHTML
If you really really do need to use outerHTML, you can simply grab $(this).prev() again:
$("#changeDiv").click(function(){
var div = $(this).prev();
div[0].outerHTML = `<div id="imSecondtDiv"> <p> World </p> </div>`;
// the "new" div is now before the button, so grab the reference of THAt one
div = $(this).prev();
// the following line does not affect the newly added division
// since the var `div` references the old DOM object
// unless I add div = $(this).prev(); before setting the html of
// the paragraph it will not set it
div.find('p').html('Override');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="imFirstDiv"> <p> Hello </p> </div>
<button id="changeDiv" >Change Div 1</button>

How to remove span onclick or on modify?

How do I write a JS function to remove the span on click and just retain the inner text ?
​<span class="test" onclick="removespan(this);">Data in red</span>
​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
​removespan = function(e)​{
alert(e.innerText);
}​
CSS : ​span.test{color:red;}
onclick I would like to remove the span and just retain the inner text .
Infact I would like to have a onmodify event ...that removes the span .
The purpose is to remove a spellchecker span class in WYSIWYG editor .
If the span is the only child element inside its parent node
<div>
<span class="test" onclick="removespan(this);">Data in red</span>
</div>
removespan = function(span) {
span.parentNode.innerHTML = span.innerHTML;
}
Otherwise use this function
removespan = function(span) {
span.parentNode.replaceChild(document.createTextNode(span.innerHTML), span);
}
In case anyone is interested in an "expanded" version of Diode's second option:
function removespan(span) {
// Get the text contents of the clicked span
var span_contents = span.innerHTML;
// Get the parent node of the clicked span
var span_parent = span.parentNode;
// Create a new text node in the DOM to hold the text contents
var text_node = document.createTextNode(span.innerHTML);
// Replace the clicked span node with your newly created text node
span_parent.replaceChild(text_node, span);
// Alternatively, do the above in one line...
// span.parentNode.replaceChild(document.createTextNode(span.innerHTML), span);
}
<span id="test"></span>
y=doc.getElementsById("test");
y.getParent().removeChild(y);

Categories