I'm looking for a pure JS way of finding the offset of a child within it's parent.
Given the below sample:
<div>
A
<br>
short space elapsed, <b>and</b> up into this noiselessness came Ahab alone from his cabin.
<span>Taking a few turns on the quarter-deck, he paused to gaze over the side</span>
</div>
I would get 3 children, a br, b, and span. Each would need to have an offset to the start of the div - So the index of how many characters into the div the start of the tag is.
So the br would have an offset of 2.
My initial idea was to get all the children of the div, then somehow from that be able to easily get an index.
function getChildrenOffset(parent){
var childNodes = parent.childNodes;
var childrenLocations = [];
var offset = 0;
var tagIndex = 0;
for(var d = 0; d < childNodes.length; d++){
var node = childNodes[d];
if(node.tagName !== undefined){
// This is a tag
tagIndex += 1;
var curLocation = new OffsetData(offset, tagIndex, node.tagName);
childrenLocations.push(curLocation);
offset += node.outerHTML.length;
}else{
// Just text
offset += node.length;
}
}
return childrenLocations;
}
function OffsetData(offset, index, tag){
this.Offset = offset;
this.Index = index;
this.TagName = tag;
}
Related
I'm trying to come up with a js snippet for devtools that will automatically add some information within some input tags.
What I've done:
var labels = [
"navigationStart",
"unloadEventStart",
"unloadEventEnd"];
var newRuleXpath = "//div[#class='btn-group dropdown']/button[#class='btn btn-primary']";
var formControlClass = "form-control ng-pristine ng-untouched ng-empty ng-invalid ng-invalid-required";
String.format = function() {
var s = arguments[0];
for (var x = 0; x < arguments.length - 1; x++) {
var reg = new RegExp("\\{" + x + "\\}", "gm");
s = s.replace(reg, arguments[x + 1]);
}
return s;
}
function getElementByXpath(path) {
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
function wait(ms){
var start = new Date().getTime();
var end = start;
while(end < start + ms) {
end = new Date().getTime();
}
}
function addRule(rule) {
var resource = rule.startsWith("resources");
if (resource == true){
var regexp = String.format("<span id='customextraction-{0}'>((?:[0-9]|.)*?)</span>", rule);
}
else {
var regexp = String.format("<span id='customextraction-perftimings-{0}'>((?:[0-9]|.)*?)</span>", rule);
}
getElementByXpath(newRuleXpath).click();
var elems = document.getElementsByClassName(formControlClass)
elems[0].value = rule
elems[1].value = regexp
}
for (var i = 0; i < labels.length; i++){
let rule = labels[i]
addRule(rule)
wait(500)
}
So, the code works fine but it only affects the last element of the labels array.
Any thoughts?
Its not that its only affecting the last element in the array. its just that its affecting the last element last. You write
var elems = document.getElementsByClassName(formControlClass)
elems[0].value = rule
elems[1].value = regexp
So every time the loop iterates, it grabs that group of elements, and applies the same elements (the first two, as you've specified getting [0] and [1]), and applies your rule. Every iteration of your loop is overwriting the last one, as you're referring the the same DOM nodes every time.
This may be a more basic question as I am learning/practicing using the DOM. But I have the following:
var demo_div = document.createElement("div")
demo_div.classList.add("demo")
var p_div = document.createElement("p")
var br = document.createElement("br")
for(let i = 0; i < 100; i++){
if (i % 5 == 0){
var text = document.createTextNode("meow")
}
else{
var text = document.createTextNode("woof")
}
p_div.appendChild(text)
p_div.appendChild(br)
}
demo_div.appendChild(p_div)
document.body.appendChild(demo_div)
I'm trying to create a div with the class = "demo" and then append a child node which is a paragraph to it. The paragraph will have 100 lines but every 5th line will have a different value.
That part wasn't hard at all the part I'm confused about is when I append the break to the paragraph, after each line is appended, the break doesn't actually work. Instead the break shows up at the end of the loop (As seen in the inspector). Any insight would be appreciated. Thanks.
You need to create a unique <br> element for each line break that you
want applied. Currently you're reusing the same <br> element which causes that same element to shift from it's last position to the next point in the text that you place it (via appendChild()).
Consider adding var br = document.createElement("br") inside your loop construct as shown:
var demo_div = document.createElement("div")
demo_div.classList.add("demo")
var p_div = document.createElement("p")
for(let i = 0; i < 100; i++){
if (i % 5 == 0){
var text = document.createTextNode("meow")
}
else{
var text = document.createTextNode("woof")
}
// Create a new br element for each loop iteration, and append it
// to your div like so:
var br = document.createElement("br")
p_div.appendChild(text)
p_div.appendChild(br)
}
demo_div.appendChild(p_div)
document.body.appendChild(demo_div)
You're only every creating one br - see how its creation is outside of the loop, rather than inside of the loop. When you call appendChild with an element that already exists in the DOM, the element is removed from its previous location and appended to the new parent.
Create the <br> inside the loop instead, just like you're doing with text:
var demo_div = document.createElement("div")
demo_div.classList.add("demo")
var p_div = document.createElement("p")
for(let i = 0; i < 100; i++){
var br = document.createElement("br")
if (i % 5 == 0){
var text = document.createTextNode("meow")
}
else{
var text = document.createTextNode("woof")
}
p_div.appendChild(text)
p_div.appendChild(br)
}
demo_div.appendChild(p_div)
document.body.appendChild(demo_div)
I'm trying to grab all the elements on my webpage using javascript. Once I do this I want to hide a message in the elements, probably by LSB. Right now I'm just trying to hide 'h' which would be 8 bits, which would require 8 elementIDs I think.
My plan is to grab all the IDs. Use charCodeAt to grab the value of 'h', then hide h's bits in the first 8 elementIDs using bitwise operations on the text size or width field.
I know this is an awful way to hide a message, but I really want to figure this out for learning purposes. Any ideas on how to do this, or direction would be awesome.
Here is what I have so far...
$(function() {
//on button click
// 1. grab LSB all of elements to be changed
// 2. change all elements
// str.charCodeAt(0);
//var arrayMsg = hideMsg.split("");
/*
// 1. maybe grab all elements on the page
// 2. make a special class with width and text
// 3. inject all element IDs with this class
// 4. flip this classes width/text size with LSB of message to hide
var allElements = document.getElementsByTagName("*");
var allIds = [];
for (var i = 0, n = allElements.length; i < n; ++i) {
var el = allElements[i];
if (el.id) { allIds.push(el.id); }
}
*/
var hideMsg = "f";
var n = hideMsg.length;
var i;
var j;
var holder;
var hideHolder;
// on button click - hide msg
$('#btnHide').on('click', function() {
for(i = 0; i < n; i++) {
//set var holder to first value of the message to hide
holder = hideMsg.charCodeAt(i);
for(j = 7; -1 < j; j--) {
//set hideHolder to holders value
hideHolder = holder;
//mask hideHolder to grab the first bit to hide
hideHolder = hideHolder & (1<<j);
//grab the first element ID
if(holder === 0) {
// embed the bit
// bitwise &=
} else {
//embed the bit
// bitwise ^=
}
}
}
});
});
I am using HTML5 + javascript for developing a webpage. I have an array with 100 values. And i have a 10 different HTML5 "div" components. I'm adding 1st 10 array values into 1st "div", 2nd 10 array values into 2nd "div" and similarly goes on. I am using HTML DOM to add these array values into particular "div" component.
Here i have used "if...elseif" condition & is working fine.
But i'm asked not to use "if" condition to add array values into different 'div' elements. Is there any other possible methods to do this?
My div components are 'div1','div2'.......'div10'(added in body tag)
var myArray = ['user1', 'user2', 'user3', ..., 'user100'];
for(i=0;i<myArray.length;i++)
{
var a = document.createTextNode(myArray[i]);
if(i<=10)
{
var container1 = document.getElementById('div1');
container1.appendChild(a);
}
elseif(i>10 && i<=20)
{
var container2 = document.getElementById('div2');
container2.appendChild(a);
}
...
...
...
...
else
{
var container10 = document.getElementById('div10');
container10.appendChild(a);
}
}
It's bad solution. The better one is following:
for(j=0;j<10;j++)
{
//get div1, div2, div3 etc.
var container = document.getElementById('div'+(j+1));
for(i=0;i<10;i++)
{
//get proper value
var a = document.createTextNode(myArray[i+j*10]);
//insert value into container
container.appendChild(a);
}
}
var set=myArray.length/10; /** no of sets of 10 **/
for(i=0;i<set;i++){ //loop through sets
for(int j=(i*10);j<(i+1)*10;j++){ //loop through each set 0-9, 10-19
var a = document.createTextNode(myArray[j]);
document.getElementById('div'+(i+1)).appendChild(a);
}
}
var myArray = ['user1','user2','user3',...'user100'];
for(var i = 0; i < 10; i++) {
var container = document.getElementById("div" + (i + 1));
for(var j = 0; j < 10; j++) {
container.appendChild(document.createTextNode(myArray[(i * 10) + j]));
}
}
If you don't mind array values being inserted into each div as one text node, you could do:
var div, i;
for (i = 1; i < 11; ++i) {
div = document.getElementById('div' + i);
div.innerHTML = myArray.splice(0, 10).join(' ');
}
You don't need 2 for loops. Do you?
for (var i = 1; i <= 100; i++)
{
document.getElementById('div' + ( i%10 + 1)) //this will give your target div
}
P.S: spare me... Typing on mobile...
I have a list that is built like this:
<ul id="ajaxList" class="ajaxListUL">
and then, every element of the list is like this
<li class="ajaxListLI" id="ajaxListRow_32412312">
this number 32412312 is random. Every element of the list has one
number there, like
<li class="ajaxListLI" id="ajaxListRow_32443232"> ...
<li class="ajaxListLI" id="ajaxListRow_86752312"> ...
<li class="ajaxListLI" id="ajaxListRow_35644312"> ...
etc.
every LI is clickable, and I need to make a loop to click every one.
but the list has also elements like this
<li class="ajaxListXT"> ...
that are not clickable.
What I need to do is to build an array of all LI elements on the page that IDs start by "ajaxListRow_" and click them.
I did this so far:
var children = document.getElementById('ajaxList').childNodes;
var length = children.length;
var elements = [];
var child;
for (i = 0; i < length; i++) {
child = children[i];
if (child.id.substring(0, 12) == "ajaxListRow_")
elements.push(child);
document.child.click();
}
it stops on the IF line, complaining about the id stuff.
I have tried to remove the id, without success.
At this point I don't even know if these lines are correct:
elements.push(child);
document.child.click();
for adding a LI to the array and to click that li.
How do I do that?
thanks.
function getElementsByClassName(classname, node) {
if(!node) node = document.getElementsByTagName("body")[0];
var a = [];
var re = new RegExp('\\b' + classname + '\\b');
var els = node.getElementsByTagName("*");
for(var i=0,j=els.length; i<j; i++)
if(re.test(els[i].className))
a.push(els[i]);
return a;
}
var childs = getElementsByClassName("ajaxListLI",document.getElementById('ajaxList'));
for(var i = 0; i<childs.length; i++){
childs[i].click();
}
Use .children instead of .childNodes, otherwise you'll get the empty text nodes that separate the <li> elements.
var children = document.getElementById('ajaxList').children; // .children
var length = children.length;
var elements = [];
var child;
for (var i = 0; i < length; i++) {
child = children[i];
if (child.id.substring(0, 12) == "ajaxListRow_") elements.push(child);
document.child.click();
}
Also document.child.click(); doesn't seem right. You may mean child.click();, though I'm not sure it will actually trigger the handler.
And I may be inclined to replace:
if(child.id.substring(0, 12) == "ajaxListRow_")
with this instead:
if(child.id.indexOf("ajaxListRow_") === 0)
but that's just a preference.