nextSibling returns null - javascript

I am getting an error saying that "this.nextSiblingElement.id is null" when clearly I have set the IDs for the elements as they generate. Which I admit still could use some work. I was just curious what is the cause of the nextSiblingElement's ID being returned as null. I made sure that the previous element was generated before the drop-down menu and that the menu is placed before it with previousChild.appendChild.
I can edit the code down if necessary but I was not sure how much to show for the sake of it making sense. I am only showing the relevant functions as follows:
//helper function to add elements to the form
function createNewFormElement(inputForm, feildName, elementName, elementValue, elementType){
var inputString = String(feildName + ": <input name='" + elementName + "' type='" + elementType + "' /> ");
var newElement = document.createElement('div');
newElement.style.display = 'inline';
if (feildName == "Quantity" && containerType != "Box/Bag"){
newElement.style.visibility = 'hidden';
}else{
newElement.style.visibility = 'visible';
}
if (feildName == "Quantity"){
newElement.id = "boxhide"+counter;
}else{
newElement.id = 'dynamicInput';
}
newElement.innerHTML = inputString;
newElement.value = elementValue;
document.getElementById(inputForm.id).parentNode.appendChild(newElement);
return newElement;
}
//helper function to add dropdown
function createNewDropDown(inputForm, feildName, elementName){
var dropDown, arValue;
var newElement = document.createElement('div');
newElement.id = 'dynamicInput';
newElement.style.display = 'inline';
dropDown = "<option value='" + containerType + "' selected>" + containerType + "</option>";
for (i=0; i<conArray.length; i++){
arValue = conArray[i];
dropDown = dropDown + "<option value='" + arValue + "'>" + arValue + "</option>";
}
var inputString = String(feildName + ": <select onchange='switchMain(this.nextElementSibling.id);' name='" + elementName + "' id='" + elementName + "'>" + dropDown + "</select> ");
newElement.innerHTML = inputString;
document.getElementById(inputForm.id).parentNode.lastChild.previousSibling.appendChild(newElement);
return newElement;
}
function getStyle(divName){
var temp = document.getElementById(divName).style.visibility;
return temp;
}
function switchMain(divName){
var e = document.getElementById("myContainers[]");
var strUser = e.options[e.selectedIndex].value;
if (strUser == "Box/Bag"){
var current = getStyle(divName);
//console.debug(current);
document.getElementById(divName).style.visibility = "visible";
}else{
document.getElementById(divName).style.visibility = "hidden";
}
}

You've got an inputString like
"… <select onchange='switchMain(this.nextElementSibling.id);' …>…</select> "
Now, if you execute
newElement.innerHTML = inputString;
You can't expect that the select element has any sibling elements - it's the only (element) node inside the newElement:
<div …> … <select onchange='switchMain(this.nextElementSibling.id);' …>…</select> </div>
Therefore, this.nextElementSibling will be null and you get an exception when trying to get its id property.
I made sure that the previous element was generated before the drop-down menu and that the menu is placed before it with previousChild.appendChild.
No, appendChild always inserts the new node in the end - regardless of the new parent's position in the DOM. If you want to insert an element in a special location, use insertBefore.

Related

Can't addEventListener('click', ...) to a div created programmatically

I parse the result of XMLHttprequest() into a JSON object, then for each node of that object I create a div to store the informations.
Finally I add each div as innerHTML of a parent div.
Here the relevant part
xhr.onreadystatechange = function() {//Call a function when the state changes.
if(xhr.readyState == 4 && xhr.status == 200) {
var response = JSON.parse(xhr.responseText);
var html="";
var linksDiv = document.getElementById('links');
if (response.error != true){
for (var i=0; i< response.links.length; i++){
var l = response.links[i];
var curId = l.id;
var curLink = l.link;
var curCreated = l.created_at;
var curOrigin = l.origin;
html = "<div id=\"link"+curId+"\" >"+
"<label><b>Id </b></label><label>"+curId+"</label> </br>"+
"<label><b>Link </b></label><label>"+curLink+"</label> </br>"+
"<label><b>Created </b></label><label>"+curCreated+"</label> </br>"+
"<label><b>Origin </b></label><label>"+curOrigin+"</label> </br></br>"+
"</div>";
linksDiv.innerHTML += html;
var curDiv = document.getElementById('link'+curId);
console.log("curDiv is"+'link'+curId);
curDiv.addEventListener('click', function(){
curDiv.style.background="gray";
getLink(curId);
});
}
}
}
}
unfortunately
curDiv.addEventListener('click', function(){
curDiv.style.background="gray";
getLink(curId);
});
doesn't work.
I already tried to make sure that the div exist (the console.log("curDiv is"+'link'+curId); works just fine)
and even used different ways like curdDiv.onmouseover = function(){curDiv.style.background="gray";}
If i put curDiv.style.background="gray"; outside the addEventListener() every div's background gets correctly changed.
If i put onmouseover="this.style.background='gray';" as inline property of the div tag when i generate it, it works as well, but i don't want javascript in the html since I will transform this page in a Chrome Extension and javascript must be separated
Please don't get confused from the mouseover tries, I need onclick behavior, but was just testing different thing to see if they worked.
I looked for a long time on SO for an answer, as you can see from my tries, but couldn't find something that worked for me. Probably there is something that I don't get.
Ps.
Let me know if you need a sample JSON data to test the function.
I think you should use
html = document.createElement('div');
html.id = 'link' + curId;
html.innerHTML = "<label><b>Id </b></label><label>" + curId
+ "</label> </br><label><b>Link </b></label><label>" + curLink
+ "</label> </br><label><b>Created </b></label><label>" + curCreated
+ "</label> </br><label><b>Origin </b></label><label>" + curOrigin
+ "</label> </br></br>";
html.addEventListener('click', function(){
this.style.background="gray";
getLink(this.id);
});
linksDiv.appendChild(html);
instead of
html = "<div id=\"link"+curId+"\" >"+
"<label><b>Id </b></label><label>"+curId+"</label> </br>"+
"<label><b>Link </b></label><label>"+curLink+"</label> </br>"+
"<label><b>Created </b></label><label>"+curCreated+"</label> </br>"+
"<label><b>Origin </b></label><label>"+curOrigin+"</label> </br></br>"+
"</div>";
linksDiv.innerHTML += html;
var curDiv = document.getElementById('link'+curId);
console.log("curDiv is"+'link'+curId);
curDiv.addEventListener('click', function(){
curDiv.style.background="gray";
getLink(curId);
});
Check this working code. make delay so that event will attached to element.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
function populateLink() {
var html = "";
var linksDiv = document.getElementById('links');
for (var i = 0; i < 10; i++) {
var l = 'l-' + i;
var curId = i;
var curLink = 'l.link-' + i;
var curCreated = 'l.created_at_' + 1;
var curOrigin = 'l.origin_' + i;
html = "<div id=\"link" + curId + "\" >" +
"<label><b>Id </b></label><label>" + curId + "</label> </br>" +
"<label><b>Link </b></label><label>" + curLink + "</label> </br>" +
"<label><b>Created </b></label><label>" + curCreated + "</label> </br>" +
"<label><b>Origin </b></label><label>" + curOrigin + "</label> </br></br>" +
"</div>";
linksDiv.innerHTML += html;
//var curDiv = document.getElementById('link' + curId)
//curDiv.addEventListener('click', function () {
// this.style.background = "gray";
// getLink(this.id);
//});
attachEvent(curId);
}
}
function attachEvent(curId) {
setTimeout(function () {
var curDiv = document.getElementById('link' + curId)
curDiv.addEventListener('click', function () {
this.style.background = "gray";
//getLink(this.id);
});
}, 100);
}
window.onload = populateLink;
</script>
</head>
<body>
<div id="links"></div>
</body>
</html>

Get text and id from an li element on click with pure JS

I've been stuck with this for several days and I can't solve it.
I've done it with jQuery with no problem, but I need it in pure JS.
This is how my list is generated.
function get_friends(items){
if(items != undefined){
if (items.length != 0){
var html_friends_list = "";
for(var count = 0; count < items.length; count++){
if(items[count].subscription == "both"){
var display_name = Strophe.getNodeFromJid(items[count].jid);
html_friends_list = html_friends_list + "<li style='font-size:19px' id='open_chat-" + items[count].jid + "'>" + "<a href='chat-js/index.html'>" + display_name + "<span class='block-list-label' id='" + items[count].jid + "_unread_messages" + "'>0</span><span class='block-list-label' id='" + items[count].jid + "_change_status" + "'></span></a></li>";
}
}
document.getElementById("friends-list").innerHTML = html_friends_list;
As a said I want to save the value of the text and the id of any li element clicked.
Regards
you haven't specified whether this is for a specific list or just any li on your page. The below will log the id and innerHTML components of any li on the page. Perhaps you may need to update the querySelector for your particular use case.
var list = document.querySelectorAll('li');
Array.prototype.slice.call(list).forEach(function(listItem){
listItem.addEventListener('click', function(e){
console.log(this.id);
console.log(this.innerHTML);
});
});
Here's a JSFiddle which I think demonstrates what you are trying to achieve.
Jsfiddle
Combination of james' answer and working example.
function get_friends(items) {
if (items != undefined) {
if (items.length != 0) {
var html_friends_list = "<ul>";
for (var count = 0; count < items.length; count++) {
if (items[count].subscription == "both") {
html_friends_list = html_friends_list + "<li id='open_chat-" + items[count].jid + "'>"+ items[count].display_name +"</li>";
}
}
html_friends_list = html_friends_list + '</ul>'
document.getElementById("friends-list").innerHTML = html_friends_list;
}
}
}
Note: you should trigger prototype after your dom element created.

Fastest way to replace big drop down list in IE8

I have to manage big drop down list (thousands of items) and I encounter performance problem with IE8 with the jQuery .html method.
Indeed it takes 3-4seconds to clear the content.
Do you have any workarounds ?
Code :
var selectHtml = "";
$(data.items).each(function () {
var option = "<option value='";
option += this.Value + "'";
if (this.Selected) {
option += " selected";
}
option += ">" + this.Text + "</option>";
selectHtml += option;
});
$(target).html(selectHtml);
.html of jQuery call .empty and in the IE profiler I can see that it is .empty that takes most of the time.
Assuming you mean something like
<ul id='mylist'>
<li>Item 1</li>
....
<li>Item n</li>
</ul>
or the equivalent select/option statement, you need:
$('#mylist').empty()
Alternatively, if you're only actually changing a few items in your dropdown list, perhaps you should maintain a map between the data.value and the element in the select list, so you only need to add items which have not already been placed in the list, and have a simple reference to items to remove.
I suspect you are wrong about the time split and most of the time is building the list. Try pushing all your new option items onto an array and then performing a single join of the array at the end.
var list = [];
$(data.items).each(function () {
var selected = this.Selected ? ' selected' : '';
var option = "<option value='" + this.Value + "'" + selected + ">"
+ this.Text + "</option>";
list.push( option);
});
$(target).html(list.join( "\n"));
I found the solution here : https://stackoverflow.com/a/23552898/1431524
I ended up with this code :
function clearOptions(select) {
var selectParentNode = select.parentNode;
if (selectParentNode) {
var newSelect = select.cloneNode(false); // Make a shallow copy
selectParentNode.replaceChild(newSelect, select);
return newSelect;
}
return undefined;
}
function appendSelectHtml(data, target) {
var selectHtml = [];
$(data.items).each(function () {
var selected = this.Selected ? ' selected' : '';
var option = "<option value='" + this.Value + "'" + selected + ">" + this.Text + "</option>";
selectHtml.push(option);
});
target = $(clearOptions(target[0])); //The item that was contained in the selector isn't in the DOM anymore
target.append(selectHtml.join(""));
}

Modifying innerHTML in nested get() jQuery

I'm currently using the jQuery get method to read a table in another page which has a list with files to download and links to others similar webpages.
$.get(filename_page2, function(response, status){
var data = $("<div>" + response + "</div>");
var target_element = data.find(target_element_type_page2 + '#' + target_element_id_page2)[0];
var container = document.getElementById(element_change_content_page1);
if (typeof target_element !== "undefined"){
var rows = target_element.rows;
for (var i = 1, n = rows.length; i < n; i++) {
var table = rows[i].cells[1].getElementsByTagName("TABLE")[0];
var isFolder = table.getAttribute("CType") == "Folder";
var elem = table.rows[0].cells[0];
var text = elem.innerText || elem.textContent;
var link = elem.getElementsByTagName("A")[0].getAttribute("href");
if (!isFolder) {
container.innerHTML += "<li class=\"mainfolderfile\">" + "<a class=\"filelink\" href=\"" + link + "\">" + text + "</a></li>";
} else {
container.innerHTML += "<li class=\"folderlist\">" + "<a class=\"folderlink\" onclick=\"open_submenu(this)\" href=\"#\">" + text + "</a><ul></ul></li>";
var elem_page1 = container.getElementsByTagName("li");
var container_page1 = elem_page1[elem_page1.length - 1].getElementsByTagName("ul")[0];
create_subfolder(container_page1, link);
}
}
} else {
container.innerHTML += "<li class=\"mainfolderfile\">" + "<a class=\"filelink\" href=\"" + "#" + "\">" + "Error..." + "</a></li>";
}
}, page2_datatype);
This is working fine, and all the folders and files are being listed. But when I try to do the same thing with the folders (calling the create_subfolder function) and create sublists with their subfolders and files, I'm getting a weird behavior.
function create_subfolder(container2, link1) {
$.get(link1, function(response, status){
var data = $("<div>" + response + "</div>");
var target_element = data.find("table" + "#" + "onetidDoclibViewTbl0")[0];
if (typeof target_element !== "undefined"){
var rows = target_element.rows;
for (var i = 1, n = rows.length; i < n; i++) {
var table = rows[i].cells[1].getElementsByTagName("TABLE")[0];
var elem = table.rows[0].cells[0];
var text = elem.innerText || elem.textContent;
var link2 = elem.getElementsByTagName("A")[0].getAttribute("href");
//nothing is changed in the webpage. The modifications in the html don't appear
container2.innerHTML += "<li>" + text + "</li>";
}
}
alert(container2.innerHTML); // Print the html with all the modifications
}, "html");
}
The second get(), inside the create_subfolder() function are not changing anything in the webpage, so no sublist is created. But, when I call the alert() function at the end of the get() function, it prints the code with all the modifications it should have made in the html at the second get callback. I believe the problem is related with the asynchronous behavior of the get function but I don't know exactly why. Any guess?

Using a list position as a param to a function to pull certain data

function getList()
{
var string2 = "<img src='close.png' onclick='removeContent(3)'></img>" + "<h4>Survey Findings</h4>";
string2 = string2 + "<p>The 15 Largest lochs in Scotland by area area...</p>";
document.getElementById("box3text").innerHTML = string2;
var myList = document.getElementById("testList");
for(i=0;i<lochName.length;i++)
{
if(i<3)
{
var listElement = "<a href='javascript:getLoch(i)'>" + "Loch "+ lochName[i] + "</a>";
var container = document.getElementById("testList");
var newListItem = document.createElement('li');
newListItem.innerHTML = listElement;
container.insertBefore(newListItem, container.lastChild);
}
else
{
var listElement = "Loch "+lochName[i];
var container = document.getElementById("testList");
var newListItem = document.createElement('li');
newListItem.innerHTML = listElement;
container.insertBefore(newListItem, container.lastChild);
}
}
}
This function generates a list with the 1st 3 elements being hyperlinks. When clicked they should call a function call getLoch(i) with i being the position of the item in the list. However when i pass it the value it just give it a value of 15, the full size of the array and not the position.
function getLoch(Val)
{
var str = "<img src='close.png' onclick='removeContent(4)'></img>" + "<h4>Loch " + lochName[Val] +"</h4>";
str = str + "<ul><li>Area:" + " " + area[Val] + " square miles</li>";
str = str + "<li>Max Depth:" + " " + maxDepth[Val] + " metres deep</li>";
str = str + "<li>County:" + " " + county[Val] + "</li></ul>";
document.getElementById("box4").innerHTML = str;
}
There are 2 errors in your code as far as I can see. The first is the way you create your link.
var listElement = "<a href='javascript:getLoch(i)'>" + "Loch "+ lochName[i] + "</a>";
This will actually result in code like this:
<a href='javascript:getLoch(i)'>Loch name</a>
Passing a variable i is probably not what you intended, you want it to pass the value of i at the time your creating this link. This will do so:
var listElement = "<a href='javascript:getLoch(" + i + ")'>" + "Loch "+ lochName[i] + "</a>";
So why does your function get called with a value of 15, the length of your list? In your getList function, you accidently made the loop variable i a global. It's just missing a var in your loop head.
for(var i=0;i<lochName.length;i++)
After the loop finished, i has the value of the last iteration, which is your array's length minus 1. By making i a global, and having your javascript code in the links use i as parameter, getLoch got called with your array length all the time.

Categories

Resources