Javascript -function won't add paragraph after every article - javascript

I want to add a paragraph after every element of type article using the function add in Javascript but it doesn't work . Here is the code I wrote :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<article> here is one article
</article>
<article> here is the second article
</article>
<script type ="text/javascript">
function add(info)
{
var art = document.getElementsByTagName('article');
var par = document.createElement('p');
par.textContent = info;
var i;
for(i in art)
{
art[i].appendChild(par);
}
};
add("ex");
</script>
</body>
</html>
The ouput I get is :
here is one article
here is second article
ex
Any help would be appreciated ! Thanks!

You've created only one Node, and then you try to "insert" it to every article there is. The node doesn't get cloned automatically, so it gets inserted only to the last element on the articles list. You have to clone your original node-to-insert to make this work:
art[i].appendChild(par.cloneNode(true));
(the true flag in cloneNode clones the node recursively, without it you would have to attach the text on each node copy by hand)
A couple of things to note in your example:
Your loop for(i in art) is not exactly safe to use, as you don't check if the element referenced is actually an own member of the list (you don't want to reference art's prototype members).
Instead, you could simply use for(var i = 0; i < art.length; i++).
You are actually inserting your paragraphs inside your articles. To properly append elements, there is a SO answer with clear explanation of how to do this without using additional libraries.

You only ever create one paragraph. Each time you go around the loop you put that paragraph at the end of the article (moving it from wherever it was before).
You need to create the paragraph inside the loop if you want to create more than one of them.

You are using the same DOM to place each time in loop. So create new DOM in each iteration of loop. Try out following.
<script type ="text/javascript">
function add(info)
{
var art = document.getElementsByTagName('article');
var i;
for(i in art)
{
var par = document.createElement('p');
par.textContent = info;
art[i].appendChild(par);
}
};
add("ex");
</script>

Related

How to generate a clickable array/list in HTML using JavaScript

I'm trying to make a list to render on an HTML page using a JavaScript function. I know that I can use the DOM.createElement("LI"), but I want each item of the list to have a different attribute so that, upon clicking that piece of the list, it re-iterates the function anew.
Here's my current HTML (I'm using the argument of 5 as an example, I'd really have a collection of possible numbers):
<!DOCTYPE html>
<html>
<body>
<p id="count" onclick="makeAList(5)">Click me too!</p>
<script>
function makeAList(num){
var itemList = [];
for (i = 0; i < num; i ++){
itemList.push(i);
itemList.forEach(document.setAttribute("onclick", makeAList(i)) = i);
}
document.getElementById("count").innerHTML = itemList;
}
</script>
</body>
</html>
I can't get anything to generate when I click on the initial page. How should I fix this? I want it so, when I click on Click me too! it will generate 0,1,2,3,4 and then if I click on, say 3, it renders 0,1,2.
There are a few things that need to be changed here.
First, in this scenario I would use addEventListener instead of setAttribute.
Next, this line document.setAttribute("onclick", makeAList(i)) will give you a Maximum call stack size exceeded. error because by writing the function like so makeAList(i) will invoke it then and there, thus continually calling the same function. You can just write document.setAttribute("onclick", makeAList). No argument is needed because, in the next part, you will be able to get the argument from the click event itself.
Next, I would create a new element for each of the items in the array, this way you can determine how many iterations to do based on the innerText of each item. Without doing this, regardless of whether or not you click 0, 1, or 2, your target's innerText will be 0,1,2 so you won't be able to adequately determine the right amount of iterations to perform. You separate them into different elements, those elements each get an onclick event added to them. And now, when you click that element, in makeAList you will have access to that element's event.target.innerText which, after using parseInt will give you an integer you can use as the iteration length.
Finally, once you have an array of generated elements based on the iteration length gathered from the event, you can clear the innerHtml of your target (document.getElementById("count")) by setting it equal to ''. Then you loop through your array of elements and use append to append them to your target.
You can also adjust your formatting by setting the CSS of your elements to display; inline-block; and using the below conditional determine where to add a comma.
p.innerHTML = i < ln - 1 ? i + ', ' : i
function makeAList(){
var itemList = [];
var ln = parseInt(event.target.innerText) >=0 ? parseInt(event.target.innerText) + 1 : 3
for (i = 0; i < ln; i ++){
var p = document.createElement('P')
p.innerHTML = i < ln - 1 ? i + ', ' : i
p.addEventListener("onclick", makeAList)
itemList.push(p);
}
document.getElementById("count").innerHTML = ''
itemList.forEach((x)=>{
document.getElementById("count").append(x)
})
}
p {
display: inline-block;
}
<!DOCTYPE html>
<html>
<body>
<p id="count" onclick="makeAList()">Click me too!</p>
</body>
</html>

Get DOM Element with jQuery

I have a web page that is structured like this:
<canvas id="myCanvas"></canvas>
#for(var i=0; i<10; i++) {
<div class="my-element">
<canvas></canvas>
</div>
}
This code generates one main canvas. Below it, 10 other dynamically generated divs are being generated. In reality, this loop is just used to show that I have some code being dynamically generated. The main thing to understand is the my-element piece.
In my javascript, I have the following:
$(function() {
var mainCanvas = document.getElementById('myCanvas');
initializeCanvas(mainCanvas); // This works.
var dynamicElements = $('.my-element');
for (var i=0; i<dynamicElements.length; i++) {
initializeCanvas($(dynamicElements[i])[0]); // This does not work
}
});
function initializeCanvas(canvas) {
// do stuff
}
The first call to initializeCanvas works because I'm passing in an actual HTML Dom element. But the second initializeCanvas, which is called multiple times, fails because it's passing in something else. How do I get the same type of element as what's returned by document.getElementById in this case?
Thanks!
First of all, this doesn't make sense:
$(dynamicElements[i])[0]
You are getting jQuery element, unwrapping it, then wrapping again in jQuery...
what you simply need to do is get canvas from the element
dynamicElements.eq(i).find('canvas')[0] should do the job
You can't use the same element for this purpose. I suggest you to clone it. Like;
var dynamicElements = $('.my-element').clone();
Also when you add more element with my-element class this will be messy. You should make sure to select only one element with $('.my-element') selection.

Creating a div within an existing div in javascript issues

I have a problem, I wanted to create a div in html as a container and in javascript create new divs within the container based on a number input from a user prompt.
My html and javascript look like this.
HTML:
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="stylesheet.css">
<title>Sketchpad</title>
</head>
<body>
<button type="button">Reset</button>
<div class= "container">
</div>
<script src="javascript.js"></script>
<script src="jQuery.js"></script>
</body>
JS
var row = prompt("Enter number of rows:");
var column = prompt("Enter number of columns:");
function createGrid(){
var cont = document.getElementsByClassName('container');
for(i=1; i<column; i++){
var sketchSquare = document.createElement('div');
cont.appendChild(sketchSquare);
}
}
createGrid(column);
I end up with this error: Uncaught TypeError: cont.appendChild is not a function.
I imagine this is something to do with the getElementsByClassName?
I do have a solution which involves creating the container div in javascript and appending the smaller squares inside the container div. I was just curious as to why my first soltuion didn't work?
cont[0].appendChild(myDiv) is a function.
When you document.getElements By Class Name as the name implies you are getting many elements (an array of sorts) of elements and this array don't have the same functions as each of its elements.
Like this:
var thinkers = [
{think: function(){console.log('thinking');}
];
thinkers don't have the method .think
but thinkers[0].think() will work.
try this: open your javascript console by right clicking and doing inspect element:
then type:
var blah = document.getElementsByClassName('show-votes');
blah[0].appendChild(document.createElement('div'));
It works!
also if you want to use jQuery which I do see you added...
you can do:
var cont = $('container');
cont.append('<div class="sketchSquare"></div>');
Try that out by doing this:
First get an environment that has jQuery.
Hmm maybe the jQuery docs have jQuery loaded!
They do: http://api.jquery.com/append/.
Open the console there and at the bottom where the console cursor is type:
$('.signature').append('<div style="background: pink; width: 300px; height: 300px"></div>');
You'll notice that you add pink boxes of about 300px^2 to 2 boxes each of which have the "signature" class.
By the way, prompt gives you a string so you'll have to do row = Number(row); or row = parseInt(row, 10); and another thing don't use that global i do for(var i = 0; ...
var cont = document.getElementsByClassName('container');
Because that^ doesn't return a node, it'll return an HTMLCollection.
https://www.w3.org/TR/2011/WD-html5-author-20110705/common-dom-interfaces.html#htmlcollection-0
You need to pick an individual node from that collection before appending.
There could be a couple of issues that could cause this. Without fully giving the answer here's what it could be at a high level.
Your script is ran before the DOM is fully loaded. Make sure that your script is ran after the DOM is present in the page. This can be accomplished using either the DOMReady event ($(document).ready equivalent without jQuery) or simply making sure your script tag is the last element before the closing body tag. (I usually prefer the former)
When you utilize document.getElementsByClassName('container') (https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName) this method returns an array therefore you would either need to apply the operation to all elements of the result or just select the zero-th as document.getElementsByClassName('container')[0]. As an alternative, if you would like to be more explicit you could also place an id on the container element instead to more explicitly state which element you would like to retrieve. Then, you would simply use document.getElementById([id]) (https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById) and this would get back a single element not a collection.
The result of prompt is a string. Therefore you would have to first parse it as an integer with parseInt(result, 10) where 10 is simply the radix or more simply you want a number that is from 0-10.
You should include jquery library before your script, it`s important
<script src="jQuery.js"></script>
<script src="javascript.js"></script>

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

string replace without href in javascript

<html>
<head>
<title>Test</title>
</head>
<body>
hello
<script>
var str=document.body.innerHTML;
document.body.innerHTML=str.replace(/hello/g, "hi");</script>
</body>
</html>
In this code hello.html and hello will change hi.html and hi. I don't want to replace href="". How to write regular expression for that ?
The following regex replace wil do what you want:
<script>
var str=document.body.innerHTML;
document.body.innerHTML=str.replace(/(>[^<]*)hello/g, "\1hi");
</script>
But I think it is still fragile, and any solution with regex replaces in .innerHTML will be... Remember that regexes are always a hacky solution when trying to solve problems which involve html/xml parsing.
What do you need this for? Am I guessing correctly when I say that you want to replace all the text content of the document?
In that case, I would suggest getting a list of all content nodes from the DOM (see this question’s accepted answer for two ways to do this, one with jQuery and one without).
Using that, you could then apply your function to update each text node's contents:
var textNodes = getTextNodesIn(el);
for (var i = 0; i < textNodes.length; i += 1) {
textNodes[i].innerHTML = textNodes[i].innerHTML.replace(/hello/g, "hi");
}
This would leave all the HTML attributes unaffected. If you want to adjust those as well (excepting, of course, any href attribute), you could expand the getTextNodes function to include attributes (excepting href attributes) in the returned list of nodes.

Categories