Unable to get element in javascript - javascript

I have the following HTML data on a website which I am trying to manipulate using an extension in Google Chrome:
<div id="lvl-2">
<div class="loading" style="display: none;"><div class="loading-text"><div class="nowloading">Loading…</div>
<div class="content">
<div class="dirs">
<form class="search">
<input type="text">
</form>
<div class="dirs-list"></div>
</div> <!-- dirs -->
<div class="tunes"><table>
<tbody>
<tr></tr>
...
<tr></tr>
</tbody></table>
</div>
</div>
I am using the following code to get the table element somehow:
var div1 = document.getElementById("lvl-2");
var divChild1 = div1.children;
var divChild2 = divChild1[1].children;
var tables = divChild2[1].children;
But on executing the line where the tables variable is assigned, I am getting this error:
Cannot read property 'children' of undefined.
I am not sure why am I getting this error, as second child of the divChild2 variable should contain the table as a child. What do you suggest I should do?
Ultimately, my aim is to randomize all the rows in the table. If there is another way I can do that, I would welcome it.
P.S. 1: I am a Javascript noob, I think it is abundantly clear.
P.S. 2: Apologies for the weird HTML. I had to remove a lot of useless tags in between the relevant tags. Otherwise the HTML would have gotten huge.

Your HTML is invalid, you are not closing the .loading div and some others. So your DOM is absolutely not the way you expect it to be.
That is why all those stupid people always tell you to indent your code properly. Might be easier to catch problems like this. Validation is also a good way to find problems like this.
This is how your HTML looks after running it through a beautifier (which you could do too before asking a question next time):
<div id="lvl-2">
<div class="loading" style="display: none;">
<div class="loading-text">
<div class="nowloading">
Loading…
</div>
<div class="content">
<div class="dirs">
<form class="search">
<input type="text">
</form>
<div class="dirs-list">
</div>
</div>
<!-- dirs -->
<div class="tunes">
<table>
<tbody>
<tr>
</tr>
<tr>
</tr>
</tbody>
</table>
</div>
</div>
Obviously this is not what you expected. I fixed it for you:
<div id="lvl-2">
<div class="loading" style="display: none;">
<div class="loading-text">
<div class="nowloading">
Loading…
</div>
</div>
</div>
<div class="content">
<div class="dirs">
<form class="search">
<input type="text">
</form>
<div class="dirs-list">
</div>
</div>
<!-- dirs -->
<div class="tunes">
<table>
<tbody>
<tr>
</tr>
<tr>
</tr>
</tbody>
</table>
</div>
</div>
</div>
And with the fixed HTML, your code works.

In your webpage, there are other elements that you don't know. Each text (including line breaks, tabs and spaces) between tags is a TextNode, which is a Node in the DOM document.
var div1 = document.getElementById("lvl-2");
// divChild1 is a TextNode representing the line break and the tab
// between <div id="lvl-2"> and <div class="loading"
var divChild1 = div1.children;
// A textNode doesn't have a children attribute, so divChild2 is undefined
var divChild2 = divChild1[1].children;
// Error
var tables = divChild2[1].children;
To solve it, you can try using "firstElementChild" and "nextElementSibling", that only refers to "true" Nodes (assuming you try to get the "tunes" div. I haven't tested it but it should work) :
var div1 = document.getElementById("lvl-2");
var divChild1 = div1.firstElementChild;
var divChild2 = divChild1.nextElementSibling;
var tables = divChild2.nextElementSibling;

Related

Finding and replacing attribute in HTML File

I have a project folder which contain list of HTML file which contain some knockout syntax , I am looking some pointer where are I search for certain pattern of attribute and replace it will new syntax.
I tried something with JSDOM but was not successful.
Structure of HTML file is something like this , here I have find all occurrences of data-bind="attr :{id : <>}" and replace it with :id="[[componentid]]"
<div data-bind="attr :{id :componentid}" class="">
<div class="formlayout">
<input type='textbox>
</div>
</div>
<div data-bind=" attr :{id : pageid}" class="">
<div class="formlayout">
<input type='textbox>
</div>
</div>
After the change HTML file should look like below.
<div :id="[[componentid]]" class="">
<div class="formlayout">
<input type='textbox>
</div>
</div>
<div :id="[[pageid]]" class="">
<div class="formlayout">
<input type='textbox>
</div>
</div>
Note: There can be multiple whitespace for the data-bind property we should ignore those. I was trying with JSDOM and node js but was not successful and pointer will be really helpful
you can try this...
<script>
const getElement1 = document.querySelector(".data1");
const getElement2 = document.querySelector(".data2");
getElement1.removeAttribute("data-bind");
getElement1.setAttribute(":id", "[[componentid]]");
getElement2.removeAttribute("data-bind");
getElement2.setAttribute(":id", "[[pageid]]");
</script>

modify / move an existing html tag in the DOM (javascript)

I want to move my div class="contentlinks" tag with its content, to place it in the div class="row" after the div class="col-12".
Here is the basic html architecture :
<span id="data">
<section id="" class="thesectionQ">
<div class="container">
<div class="row">
<div class="col-12">
<h1>Title</h1>
</div>
</div>
</div>
</section>
<div class="col-12 contentlinks">
<a class="btn">link1</a><a class="btn">link2</a>
</div>
</span>
Tags are dynamically created in js... my problem is that I can't seem to use appendChild ...
my problem is that I can't seem to use appendChild ...
I'm trying to target my section with a class="thesectionQ" in a variable as well as my div class="contentlinks" that I want to move :
for example...
var thedata2 = document.getElementById('data').getElementsByClassName('thesectionQ');
var thedata = document.getElementById('data').getElementsByClassName('contentlinks');
thedata2.appendChild(thedata);
but I have errors ... and too often
give section id so it become easy.
document.querySelector('#sectionId div.row')
.appendChild(document.querySelector('div.contentlinks'))

auto complete on editable divs angular js

I am not able to perform the auto suggestion functionality on divs with contenteditable attribute. Also when I write mass-autocomplete to divs, it is showing an error message like "mass-autocomplete not allowed on element div".
The following is code I have written. Could you please give the solution for this?
$scope.getClients = {
suggest: suggest_Client
};
<div class="row">
<div class="col-xs-12">
<div class="marginTB15" mass-autocomplete>
<div class="reach_box" contenteditable="true" ng-model="user.communities" mass-autocomplete-item="getNetworks">
<span class="form-control-feedback form-control-feedback_left_textbox"><img src="images/icon7.png" ></span>
<div class="reach"></div>
</div>
</div>
</div>
</div>
Seems that you using the mass-autocomplete directive incorrectly, it need so have an <input> tag as a element. Consider just having 2 blocks inside your div - one is a normal autocomplete:
<div mass-autocomplete>
<input ng-model="user.communities" mass-autocomplete-item="getNetworks">
</div>
while another one is a static end result of the autocompletion - say <div>{{user.communities}}</div>. And those two will be toggled by click for example. By this you wont' need content-editable at all.
So full code may look like this:
//controller
$scope.stateEdit = false;
$scope.toggleWidgetState = function(){
$scope.stateEdit = !$scope.stateEdit;
}
$scope.getClients = {
suggest: suggest_Client,
on_select:toggleWidgetState //here we put our widget back to read-only state
};
//other logic to handle mass-autocomplete
//template
<div class="row">
<div class="col-xs-12">
<div mass-autocomplete ng-show="stateEdit">
<input ng-model="user.communities" mass-autocomplete-item="getNetworks">
<ul> <li>Cardiologist Connect<button type="button">X</button></li></ul>
</div>
<div ng-show="!stateEdit" ng-click="toggleWidgetState()">
{{getNetworks}}
<span class="form-control-feedback form-control-feedback_left_textbox"><img src="images/icon7.png" ></span>
<div class="reach"></div>
</div>
</div>
</div>

Click on a div, take a text and copy paste it in a table

I have a plugin in my website and I want to customize a feature that it doesn't provide me.
<div id="4_15:00" class="DOPBSPCalendar-hour dopbsp-available">
<div class="dopbsp-bind-top">
<div class="dopbsp-hour"> </div>
</div>
<div class="dopbsp-bind-middle dopbsp-group0">
<div class="dopbsp-hour">15:00</div>
<div class="dopbsp-available">1 available</div>
</div>
<div class="dopbsp-bind-bottom">
<div class="dopbsp-hour"> </div>
</div>
</div>
<div id="4_16:30" class="DOPBSPCalendar-hour dopbsp-available dopbsp-selected">
<div class="dopbsp-bind-top">
<div class="dopbsp-hour"> </div>
</div>
<div class="dopbsp-bind-middle dopbsp-group0">
<div class="dopbsp-hour">16:30</div>
<div class="dopbsp-available">1 available</div>
</div>
<div class="dopbsp-bind-bottom">
<div class="dopbsp-hour"> </div>
</div>
</div>
This is the part of the table I want to show the selected time under the date:
<table class="dopbsp-cart">
<tbody>
<tr>
<td class="dopbsp-label">Check in</td>
<td class="dopbsp-value">19 February 2015</td>
</tr>
And here is my jQuery:
<script>
jQuery(document).ready(function () {
jQuery('.DOPBSPCalendar-hour.dopbsp-available').click(function (e) {
t = jQuery('.dopbsp-selected .dopbsp-hour').text();
table = jQuery('table.dopbsp-cart .dopbsp-value');
table.first().append("<br>"+t)
});
});
</script>
When a user select a specific time, it automatically adds the class: dopbsp-selected. So, I want to take the selected time and add it below the date. However, I don't have any error on the console and I cannot find the reason that doesn't work.
Your selectors are wrong:
jQuery('.DOPBSPCalendar-hour .dopbsp-available').click(function (e) {
^---
The space means you want nested elements, the first having .DOBetc..., and then a child element with .dobspetc.... Try
jQuery('.DOPBSPCalendar-hour.dopbsp-available').click(function (e) {
^--- no space
which indicates "any one element with these two classes applied".
The selector you are using is wrong.
.DOPBSPCalendar-hour .dopbsp-available
The space show that .dopbsp-available is nested to .DOPBSPCalendar-hour
But what you want is .DOPBSPCalendar-hour.dopbsp-available
which means a element has both the classes

Get elements just 1 level below the current element by javascript

I need to access the DOM tree and get the elements just 1 level below the current element.
Read the following code:
<div id="node">
<div id="a">
<div id="aa">
<div id="ab">
<div id="aba"></div>
</div>
</div>
</div>
<div id="b">
<div id="ba">
<div id="bb">
<div id="bba"></div>
</div>
</div>
</div>
<div id="c">
<div id="ca">
<div id="cb">
<div id="cba"></div>
</div>
</div>
</div>
</div>
I want to get the 3 elements "a", "b", "c" under "node". What should I do?
var nodes = node.getElementsByTagName("div") <---- I get all the divs but not the 3 divs I need.
var nodes = node.childNodes; <---- works in IE, but FF contains Text Node
Does anyone know how to solve the problem?
You could use a function that rules out all non-element nodes:
function getChildNodes(node) {
var children = new Array();
for(var child in node.childNodes) {
if(node.childNodes[child].nodeType == 1) {
children.push(child);
}
}
return children;
}
I'd highly recommend you look at JQuery. The task you're looking to do is straightforward in pure Javascript, but if you're doing any additional DOM traversal, JQuery is going to save you countless hours of frustration. Not only that but it works across all browsers and has a very good "document ready" method.
Your problem solved with JQuery looks like:
$(document).ready(function() {
var children = $("#node").children();
});
It looks for any element with an id of "node" then returns its children. In this case, children is a JQuery collection that can be iterated over using a for loop. Additionally you could iterate over them using the each() command.
This is simplier than you think:
var nodes = node.querySelector("node > div");
Try this (late answer, but can be useful for others):
var list;
list=document.getElementById("node").querySelectorAll("#node>div");
Universal selectors can do the trick:
var subNodes = document.querySelectorAll("#node > *");
Query parts:
#node is unique container selector
> next slector should be applied only on childs
* universal selector that match every tag but not text
Can I use universal selector
In my opinion the easiest way to do this is to add a class name to the
first level child nodes:
<div id="node">
<div id="a" class="level_1">
<div id="aa">
<div id="ab">
<div id="aba"></div>
</div>
</div>
</div>
<div id="b" class="level_1">
<div id="ba">
<div id="bb">
<div id="bba"></div>
</div>
</div>
</div>
<div id="c" class="level_1">
<div id="ca">
<div id="cb">
<div id="cba"></div>
</div>
</div>
</div>
</div>
and then to use the method getElementsByClassName, so in this case:
document.getElementById('node').getElementsByClassName('level_1');
I think node.childNodes is the right place to start. You could (to make it work with FF too), test the nodeName (and possibly nodeType) of all child nodes you get, to skip text nodes.
Also you might have a look at some javascript library like prototype, which provide a lot of useful functions.
I've added some text so we can see that it is working, and JavaScript that will add "added!" to the bottom of each of the divs at the base:
var cDiv = document.querySelectorAll('body > div > div'), i;
for (i = 0; i < cDiv.length; i++)
{
cDiv[i].appendChild(document.createTextNode('added!'));
}
<div id="node">
<div id="a">a
<div id="aa">aa
<div id="ab">ab
<div id="aba">aba</div>
</div>
</div>
</div>
<div id="b">b
<div id="ba">ba
<div id="bb">bb
<div id="bba">bba</div>
</div>
</div>
</div>
<div id="c">c
<div id="ca">ca
<div id="cb">cb
<div id="cba">cba</div>
</div>
</div>
</div>
</div>

Categories