So, I'm trying to have a button add another dropdown (DD in my code) to the page, and it works just dandy, except when I try to remove (which "works") the remaining node no longer has the parentNode set properly.
Here is my code:
function insertAfter(newNode, elem) {
elem.parentNode.insertBefore(newNode, elem.nextSibling);
}
function addSwitchMenu() {
var lastDD = dropdowns.switches.last();
var newDD = lastDD.cloneNode();
newDD.innerHTML = lastDD.innerHTML;
var oldButton = document.getElementById("add-switch");
var newButton = oldButton.cloneNode();
var newBR = document.createElement("br");
oldButton.value = '-';
oldButton.id = 'remove-switch';
oldButton.onclick = function() {
var index = dropdowns.switches.indexOf(newDD);
dropdowns.switches.splice(index,1);
lastDD.parentNode.removeChild(lastDD);
oldButton.parentNode.removeChild(oldButton);
newBR.parentNode.removeChild(newBR);
updateResults();
}
dropdowns.switches.push(newDD);
console.log(newDD);
console.log(lastDD.parentNode);
insertAfter(newDD, lastDD);
insertAfter(newButton, lastDD);
insertAfter(newBR, lastDD);
}
And basically I call this function, then I call the remove function of the first one, then I call this function once more using the node that was created with the first one. I'm guessing it's something to do with the referencing node being removed, but the new node has a parentNode up until the other node is removed. Why? And how can I fix this?
Related
In my code like ,
function ElementBase(name) {
this.tagName = typeof name != "" ? name : 'div';
this.createElem();
}
ElementBase.prototype = {
createElem: function() {
this.elem = document.createElement(this.tagName);
},
getIndex: function() {
var nodes = this.elem.parentNode.childNodes,
node;
var i = count = 0;
while ((node = nodes.item(i++)) && node != this.elem)
if (node.nodeType == 1) count++;
return (count);
}
};
I try to create the DOM element tag is "div".
function Div() {
this.tagName = 'div'
ElementBase.call(this, this.tagName);
}
Div.prototype = Object.create(ElementBase.prototype);
My Question is,
1) How to access the getIndex function from the html document after inserting the created objects?
example:
var div = new Div();
div.id = "d1"
document.body.appendChild(div.elem);
// After div.getIndex() working
Then some situation i need the index value of that div (id="d1") element from document.
var d= document.getElementById("d1");
d.getIndex() //not working
What mistakes i did it in above code?
thanks advance..
I think when you do document.body.appendChild(div.elem) you just do document.body.appendChild(document.createElement('div')) nothing more.
And when you do var d= document.getElementById("d1"); d is just an object return from the DOM that has nothing to do with your var div
what you can do is:
Div.prototype.getIndex.call(d);
But that doesn't actually extend your object. Actually extending a DOM object is a bad practice (check this http://perfectionkills.com/whats-wrong-with-extending-the-dom/).
Look closely at your code.
div is an instance of Div and it has a property .elem that holds the actual DOM element.
So when you do div.id = "d1", you are not setting the id of the DOM element.
var div = new Div();
div.id = 'd1'; // <div></div>
div.elem.id = 'd1'; // <div id="d1"></div>
But there's one more problem: when you do d= document.getElementById("d1"), what you get is a DOM element, not an instance of Div().
Since .getIndex() is defined on .prototype of Div(), plain old DOM elements don't have access to it.
How you solve this situation depends on what exactly you need to accomplish with your code.
Edit 1: In response to OP's comment:
document.getElementById() returns an instance of HTMLDivElement, which is fundamentally different from an instance of Div.
One solution is to use a setter method:
function Div() {
// ...
}
Div.prototype.setId = function setId(id) {
this.elem.id = id;
}
var div = new Div();
div.setId('d1'); // same as doing div.elem.id = 'd1';
another solution is to use id in the constructor function itself:
function Div(id) {
// ...
this.elem.id = id; // or you can use "this.setId(id)"
/*
if "id" is provided,
it will take that value,
else it is set to "undefined",
which is the same as not being set
*/
}
Div.prototype.setId = function setId(id) {
this.elem.id = id;
}
var div = new Div('d1'); // same as doing div.elem.id = 'd1';
div.setId('d2'); // same as doing div.elem.id = 'd2';
Im currently getting data from Google places and am dynamically creating the div to display the results. I am trying to add a mouseover event to each div yet am having no luck implementing it. Ive check the html source and it seems to not add the event for any of the created DIV's.
JS:
for (var i = 0; i < relatedProperties.length; i++) {
var div = document.createElement("div");
div.innerHTML = relatedProperties[i].formatted_address;
div.className = "item_holder";
div.onmouseover = PanToMarker(relatedProperties[i].formatted_address);
document.getElementById('relatedpropertyDIV').appendChild(div);
}
function PanToMarker(address) {
//Grabs the address and pans to it on the map.
}
I would imagine you need to wrap your onmouseover function like this:
div.onmouseover = function() {
PanToMarker(relatedProperties[i].formatted_address);
};
This is because when you click on the element, the onmouseover function gets called with no arguments. When you specify arguments to PanToMarker like in your question, instead of assigning the function as may be intuitive, instead it executes the function then and there, and then assigns the function's output. Wrapping the function like this assures that the function doesn't get called instead while you're trying to assign it.
When you add the onmouseover event handler in this manner, you will not see it in the dom.
I would recommend that you store the index as a data property in the DOM. Then you will be able to access it from the mouseover method ...
for (var i = 0; i < relatedProperties.length; i++) {
var div = document.createElement("div");
div.innerHTML = relatedProperties[i].formatted_address;
div.setAttribute('data-index', i);
div.className = "item_holder";
div.onmouseover = PanToMarker;
document.getElementById('relatedpropertyDIV').appendChild(div);
}
function PanToMarker() {
var selectedIndex = this.getAttribute('data-index');
map.panTo(relatedProperties[selectedIndex].geometry.location);
}
There may be performance implications since you will be appending i elements to the DOM, each of which may cause a re-draw. It might be better to create all the elements first, then append once.
var section = document.createElement('section');
for (var i = 0, length = relatedProperties.length; i < length; i++) {
var div = document.createElement("div");
div.innerHTML = relatedProperties[i].formatted_address;
div.setAttribute('data-index', i);
div.className = "item_holder";
div.onmouseover = PanToMarker;
section.appendChild(div);
}
document.getElementById('relatedpropertyDIV').appendChild(section);
function PanToMarker() {
var selectedIndex = this.getAttribute('data-index');
map.panTo(relatedProperties[selectedIndex].geometry.location);
}
I am making a simple web app. In one particular page, I have to dynamically generate a list and have a calendar after every item in the list.
Here is the code:
for (var i = 0; i < goalsObj.length; i++) {
var node = document.createElement("li");
node.setAttribute("draggable", "true");
node.setAttribute('id', 'g' + i);
node.addEventListener('click', function () {
viewGoal()
}, false);
var textnode = document.createTextNode(goalsObj[i]);
node.appendChild(textnode);
var cal = "<script>calendar()</script>";
var calen = document.createTextNode(cal);
document.getElementById("sortable").appendChild(node);
document.getElementById("sortable").appendChild(calen);
}
but the problem is that instead of the output of the calendar() function being computed and shown, I am getting just "script calendar() /script" everywhere. What's wrong? What should I do?
use
var calen = document.createElement("div");
document.getElementById("calen").innerHTML=calendar();
var scriptElement = document.createElement('script');
scriptElement.type = 'text/javascript';
scriptElement.appendChild(document.createTextNode('calendar();'));
You don't need to create a script element for this. If I understand you correctly, then you simply want to display the result of calendar(). So you can just call the function and pass the result to the createTextNode function:
var calen = document.createTextNode(calendar());
What you are doing right now is creating a string "<script>calendar()</script>" and then creating a textnode with this string as content. Nothing gets executed, because createTextNode just does that, it creates a TEXTnode.
I'm building an internal Javascript app and it has a function for creating editable lists of objects. It loops through an array of objects, and displays a summary for each one along with an edit button. It then displays some more buttons at the bottom of the list for creating new items and going to the previous screen.
All of the buttons work, expect ones created within the loop. The click event never fires for the buttons added to the DOM within the loop. The really strange thing is that if I take the edit variable and append to the DOM outside of the loop the click event does fire.
var displayArray = function (where, list, summarise, display, newItem, completion, back) {
var listing = $("<div>");
listing.addClass("Listing");
for (var i = 0; i < list.length; i++) {
var index = i;
var edit = createButton("Edit", function () {
display(list[index]);
});
var anchor = $("<p>");
anchor.text(summarise(list[index]));
anchor.append(edit); //this will create an edit button, but the click event is never fired
listing.append(anchor);
}
where.append(listing);
var create = createButton("New", newItem);
where.empty();
where.append(listing);
where.append(create);
//where.append(edit); // uncomment this and it will create a button that will display the last item in the list
if (back) { where.append(createButton("Back", back)); }
if (completion) {
completion();
}
}
var createButton = function(name, action){
var button = $("<input>");
button.attr("type", "button");
button.attr("value", name);
button.on("click", action);
return button;
}
The variables being passed into the function are as follows:
where = the html element that will display the items
list = the array of objects to display
summarise = a function that generates summary text for an object passed to it that will be displayed for this object listing
display = a function that generates the edit screen for an object
newItem = a function that generates a new object and displays the edit screen for it
completion = a function to call after the screen has been built that will generate any additional UI as needed.
back = a function to display the previous screen
The behaviour is consistent across IE9, Firefox 20.0.1, and Chrome 29.0.1547.66
I know exactly what the problem is here:
Try this.
for (var i = 0; i < list.length; i++) {
var index = i;
var edit = createButton("Edit", (function (item) {
return function () {
display(item);
};
})(list[index]));
I am having trouble with JS closures:
// arg: an array of strings. each string is a mentioned user.
// fills in the list of mentioned users. Click on a mentioned user's name causes the page to load that user's info.
function fillInMentioned(mentions) {
var mentionList = document.getElementById("mention-list");
mentionList.innerHTML = "";
for (var i = 0; i < mentions.length; i++) {
var newAnchor = document.createElement("a");
// cause the page to load info for this screen name
newAnchor.onclick = function () { loadUsernameInfo(mentions[i]) };
// give this anchor the necessary content
newAnchor.innerHTML = mentions[i];
var newListItem = document.createElement("li");
newListItem.appendChild(newAnchor);
mentionList.appendChild(newListItem);
}
document.getElementById("mentions").setAttribute("class", ""); // unhide. hacky hack hack.
}
Unfortunately, clicking on one of these anchor tags results in a call like this:
loadUserNameInfo(undefined);
Why is this? My goal is an anchor like this:
<a onclick="loadUserNameInfo(someguy)">someguy</a>
How can I produce this?
Update This works:
newAnchor.onclick = function () { loadUsernameInfo(this.innerHTML) };
newAnchor.innerHTML = mentions[i];
The "i" reference inside the closure for the onclick handlers is trapping a live reference to "i". It gets updated for every loop, which affects all the closures created so far as well. When your while loop ends, "i" is just past the end of the mentions array, so mentions[i] == undefined for all of them.
Do this:
newAnchor.onclick = (function(idx) {
return function () { loadUsernameInfo(mentions[idx]) };
})(i);
to force the "i" to lock into a value idx inside the closure.
Your iterator i is stored as a reference, not as a value and so, as it is changed outside the closure, all the references to it are changing.
try this
function fillInMentioned(mentions) {
var mentionList = document.getElementById("mention-list");
mentionList.innerHTML = "";
for (var i = 0; i < mentions.length; i++) {
var newAnchor = document.createElement("a");
// Set the index as a property of the object
newAnchor.idx = i;
newAnchor.onclick = function () {
// Now use the property of the current object
loadUsernameInfo(mentions[this.idx])
};
// give this anchor the necessary content
newAnchor.innerHTML = mentions[i];
var newListItem = document.createElement("li");
newListItem.appendChild(newAnchor);
mentionList.appendChild(newListItem);
}
document.getElementById("mentions").setAttribute("class", "");
}