Ok, You've got a lot of learning to do. I don't mean that in a bad way, but here's a vanillaJS example of how I'd tackle this:
window.addEventListener('load',function l()
{//use addEventListener, to avoid mem-leaks
"use strict";//for JSLint
var more = document.getElementById('more'),
less = document.getElementById('less'),
div = more.parentNode,//3 DOM reference, to be used by event handlers
added = [],//keep references to added elements, use as stack
rmHandle = function(e)
{//callback definition, don't bind unless less link should be usable
var rm = added.pop();
rm.parentNode.removeChild(rm);
if (added.length === 0)
{
less.removeEventListener('click', rmHandle, false);
}
e.preventDefault();
e.stopPropagation();
};
more.addEventListener('click',function(e)
{//add node:
var newP, count = added.length;
e.preventDefault();
e.stopPropagation();
if (count === 0)
{//bind less event handler here
less.addEventListener('click', rmHandle, false);
}
++count;
newP = document.createElement('p');//create node
newP.setAttribute('id','param'+count);//set id
newP.appendChild(document.createTextNode('New Paragraph #'+count));//add txt content
added.push(newP);//keep reference to node
div.insertBefore(newP, less);//append at end...
},false);
window.removeEventListener('load',l,false);//unbind load handler, this is the leak in IE
}, false);
Now, this by itself is a bit meaningless, so I've gone ahead and set up this fiddle
There are quite a few things left to be done (ie an unload event handler, hide the less link etc...)
Some clarification to help you understand the code:
addEventListener
: Instead of setting attibutes, or directly binding event handlers using onload
or onclick
, I'm adding event listeners. These have the benefit of keeping everything JS-side. You're using setAttribute('onclick'...)
somewhere in your code. This sets an attribute in the DOM, that refers back to JS. This is considered bad practice, and quite out-dated.
l
-callback. My main callback (window.addEventListener('load', function l()...
is called l
. In this function, I query the DOM three times (sort of) and I assign the references to these DOM nodes to a variable: more
, less
and div
. Next, I declare an array, to hold all nodes I'll be creating. A sort of stack, so I never need to query the dom to get a reference to those nodes I've created.
- I also declare a function (
rmHandle
), which will handle the clicks on the less
link. Because I declare this function within the scope of l
, the function has access to all variables I previously declared (less
, more
, added
). Again: I need never query the DOM...
more.addEventListener
: This link has to work from the off, so I'm attaching my event listener to this DOM node on load.
- no
return false
: Your question suggests that you know of/have used jQuery. return false
in jQ and return false
in JavaScript are not the same thing. if you attach an event handler to a form in VanillaJS return false
might leave you gobsmacked, because at times: the form will still be submitted. Read about the W3C event model: the capturing and bubbling phases. quirksmode.org is a good resource for details. You'll understand why I'm calling these methods explicitly soon enough.
document.createTextNode
: Now I will admit to using innerHTML
every now and then, too. But since you're learning, I might aswell point out that innerHTML
is not standard, the official standard is to use createTextNode
.
- At the end I remove the
load
event listener Because IE tends to leak memory if you don't. Then, the callback goes out of scope and there's nothing you can do about it. So everything can be flagged for GC, and there is no way anything can leak memory, still...
Edit:
I will admit that, listing up a few list-items, of which one just briefly touches on JS's way of resolving variable names in nested scopes isn't quite clear enough. It wasn't for me when I first started learning about closures, and it certainly isn't enough to explain why the code I posted is going to outperform yours by quite a bit.
So if you're up to it, I'm going to explain this a bit more, using a excerpt from your code, and walk you through a clean-up review:
var less = document.getElementById("less");
less.onclick = function less()
{
var para1 = document.getElementById("para1");
var para2 = document.getElementById("para2");
alert("fr");
alert( para1.innerHTML);
para1.parentNode.removeChild(para1);
para2.parentNode.removeChild(para2);
var less = document.getElementById("less");
var toMore = less.setAttribute("id", "more");
var more = document.getElementById("more");
more.setAttribute("onclick", "more(); return false;");
more.innerHTML ="click here for more";
return false;
};
This code should look familiar to you (it's copy-pasted from your question after all). Now why would I change this? First off: the DOM API (not part of JS BTW) is slow, clunky, illogical and a main source of frustration and using it too much kills woodland critters. In this snippet, we see this, though:
var less = document.getElementById("less");
less.onclick = function less()
{
//...
var less = document.getElementById("less");
}
So the name less
is being used 3 times in an assignment context. Two of those assignments involve a DOM query. Not just a query, but exactly the same query: document.getElementById("less");
! It's often said that one of the rules for writing good code is Do Not Repeat Yourself.
Another thing you might have heard is that, even when using loosely typed languages, it's not a bad idea to not assign different types to one variable. You're doing just that, though when you write function less(){}
. Apart from a few (at times significant, but that's for some other time) semantic differences, this is basically the same as doing:
var less = function(){};
Each of these assignments is masking the previous one. If wou would've written:
var less = document.getElementById("less");
less.onclick = function less_func()
{
console.log(less);//logs a dom reference!
};
//or even:
less.onclick = function()
{//anonymous or lambda functions are valid... and quite common, too
console.log(less);
};
You wouldn't need that second DOM query witing the onclick
function at all. This is because if JS's way of trying to resolve all variables to a previously declared variable. Consider this:
var evilGlobal = 'Do not use Globals';
function()
{
var goodLocal = 'Declared in function',
funcVar = function()
{
console.log(goodLocal);
console.log(evilGlobal);
},
func2 = function goodLocal(evilGlobal)
{
console.log(goodLocal);
console.log(evilGlobal);
console.log(funcVar());
};
funcVar();//logs Declared in function and Do not use Globals
func2();//logs itself (function), and undefined and then same as above
func2(goodLocal);//logs itself, Declared in Function and the same as funcVar
}
How does this come about? within funcVar
it's fairly simple:
console.log(goodLocal);//<-- JS looks inside funcVar's function scope for var goodLocal
//not found? JS looks in the outer scope, that of the anonymous function that starts
//with var goodLocal = 'Declared in Function'
//This is the var used
The same applies to console.log(evilGlobal)
. Only this time, JS scans funcVar
's scope, the anonymous function's scope and the global namespace. Why shouldn't you use globals? well, they're clearly slower, they can change state because functions can access them freely, and they clog the memory (the garbage collector only frees what is no longer referenced anywhere. The global namespace is always accessible).
The second case is a tad trickier, but not by much:
function goodLocal()//the goodLocal name is defined as the function!
this name is masks the variable in the outer scope. JS starts scanning the local scope, and finds goodLocal
to be pointing to the function. It never checks the outer scope, so it never sees the goodLocal
var in the parent function.
The same applies to evilGlobal
:
function goodLocal(evilGlobal)
An argument is a variable, declared in the scope of the function. JS will never scan the global ns, because both names can be resolves localy, except for:
console.log(funcVar());
This will result in a scope scan of the parent function, which declares the funcVar
variable, and assigns the previously discussed function to it. This function will still behave no different, as the function is called in its own scope/context.
Call contexts are quite tricky, too, so I'm going to gloss over this for a moment.
Back to your code: the other statements are actually repetitions of stuff you've written before, too: var para1
and var para2
are redundant, if you just keep them accessible in the outer scope.
Ah well, just keep reading, and keep learning, you'll get it soon enough...