I'm looking to select a range with javascript createRange function. I'm just now after so many years discovering this function, and it's one of those DOH! moments.
Is there a way to select all the elements in a given parent. Currently, I have a div full of content I'm trying to select. This content may be text nodes, elements, IDK.
var container = document.getElementById('mydiv');
var range = document.createRange();
range.setStart(container, 0);
range.setEnd(container, ???);
Is there an accurate, easy, efficient way to get the length of content in the parent (container)? Or is there a hidden method with range that does this already?
No libraries, please! Not terribly concerned with cross-browser fall-backs, but insights into them are welcome if you have any.
EDIT
For clarity, I'm NOT looking to get the innerHTML, but rather a range object. So PLEASE don't say "why not just use innerHTML".
selectNodeContents does the trick, getting the content of the specified element.
range.selectNodeContents(container);
Related
<div>
<a href='...'>LINK</a>
<img class='image' />
</div>
<div>
...
</div>
I want to get a protractor element for the img tag with image class. I already know the link text 'LINK'. In other words, "How do I locate a sibling of a given element?".
The first line of the code could look like this:
browser.findElement(by.linkText('LINK'))
Any ideas?
Thanks & Cheers
Thanks for the inspiration. Here's my solution, not the one I was hoping for, but it works:
element(by.css('???')).element(by.xpath('..')).element(by.css('???')).click();
The chaining and the by.xpath, which allows to get back to the parent are the keys of the solution.
This is what I actually implement on a Page Object:
this.widgets['select-status'] = this.ids['select-status']
.element(by.xpath('following-sibling::div[1]'));
this.widgets['select-status.dropdown'] = element(by.css('.btn-group.bootstrap-select.open'));
The page is based on Bootstrap along with Bootstrap Select. Anyways, we traverse the DOM along the following-sibling axis. Refer to XPATH specification for yourself.
Using Xpath selectors is not a better choice as it slows down the element finding mechanism.
I have designed a plugin to address this specific issues: protractor-css-booster
The plugin provides some handly locators to find out siblings in a better way and most importantly with CSS selector.
using this plugin, you can directly use:
var elem = await element(by.cssContainingText('a','link text')).nextSibling();
elem.click(); //proceed with your work
or, use this as by-locator
var elem = element(by.cssContainingText('a','link text')).element(by.followingSibling('img'));
You can always checkout the other handy methods available here...
Now, you can find web elements such as:
Finding Grand Parent Element
Finding Parent Element
Finding Next Sibling
Finding Previous Sibling
Finding any Following Sibling
Finding First Child Element
Finding Last Child Element
And, guess what, everything you can find using 💥 CSS Selectors 💥
Hope, it will help you...
So much confusion, so few answers. I'm trying to loop through the DOM, looking for a specific node by id, however, this code has several problems for which I have no explanation. First, the length of the childNodes list comes up as '5'. Two "ul"'s, two "id"'s, if those count...and one for luck?
Second, it dies at if(y[i].hasAttribute('id')===true). Firebug says this is not a function. I have no reason to not believe it, but am not sure why it isn't.
Thank you for any help.
<div id="list">
<ul id="first"></ul>
<ul id="second"></ul>
</div>
<script>
var comments=document.getElementById('list')
var y=comments.childNodes;
var count=y.length
for(i=0;i<count;i++)
{
document.write(y.length);
if(y[i].hasAttribute('id')===true)
{ document.write('here!');}
}
</script>
the childNodes attribute contains all nodes in the DOM, which specifically means, it includes text nodes. you have 3 of them - the newline/linefeed characters inside your div.
you can test for element children using the nodeType attribute ( see eg. here; 1represents ELEMENT_NODE, 3 stands for TEXT_NODE).
If you use a tool like Firebug and inspect the DOM itself, you would see all the children of an element and the difference between .children and .childNodes .
It's by hunting around in the DOM that I discovered why there are so many things that at first appear to be duplicates of each other, but are definitely not. The Mozilla developers site developer.mozilla.org is also a wealth of information.
So I'm trying to control the stacking of equally-sized elements via z-index.
Now an idea that I came across recently to avoid going through z-indices and improve performance times by hopefully to avoiding browser reflows is instead order layers via the order I append things to the parent.
So if I have a container div that holds all the stacking divs, and linked list that mirrors the order, referencing the stacking divs, then I reorder the divs based on user input. Then instead of updating the z-indices, I would recreate the div element and just append everything in order. So something like this:
var from = nodeBeforeFrom; // Input
var target = nodeBeforeTarget; // Input
var linkedlist = input; // var linkedlist contains all the stacking divs
linkedlist.moveElement(div1, div2); //Move div1 to after div2
var container = document.createElement('div');
linkedlist.reorder; //
var cur = linkedlist.first;
while (cur.next) {
container.appendChild(cur)
cur = cur.next;
}
document.removeChild(oldContainer);
document.appendChild(container);
// This is meant as pseudocode so forgive an errors in regards to the specifics
So my questions are the following:
Would this reduce browser reflows from n reflows to just 1 or 2 (where n is the number of divs)? If I understand it right, changing the z-index of a single element should cause either a browser repaint or a reflow.
Will the second approach work and stack elements in the order you append them?
Is there a way to move childs around using the DOM's child node structure already so I don't have to create a separate linked list? I only see removeChild and appendChild functions that I can use at the moment.
And yes performance is an issue since I'm planning on using this for graphics and html5 stuff. So where I can save I would like to save.
Well great it seems I've answered my own question after playing around with things and some research thanks to the good people at Opera. Pretty much yeah it's faster to perform updates on hidden/unseen elements on the browser, then to add it to the DOM tree. I got my confirmation from here. The actual trick is to set the hidden CSS tag, perform all the operations that affect display, then set hidden back to true and that reduces your browser reflows from O(n) to just 2 total.
Also this method for avoiding z-index certainly works. I unfortunately still haven't found a way to access the childNodes linked list for DOM elements. However, taking a closer look at the specification, it turns out that childNodes for DOM nodes is read-only, which likely means it's not possible unless there's some vague hack around it.
I have a treeview with drag and drop functionality. Upon completion of drag and drop I need to find the count of the child nodes of the new parent node. So for that I'm using the following
lines
var childnodelength=elem.parentNode.parentNode.childNodes.length;
alert(childnodelength);
But I always get the same value of 4 irrespective of the no. of childs, with the above alert.
I've also tried in the following way.
alert(elem.getElementsByTagName("A")[0].childNodes.length);
Above line always gives me 1 irrespective of the no. of childs. I am not sure if I am referring correctly in both the ways.
And hence I'm unable to find the no. of child nodes.
Please could someone help me with this?
Thanks
Keep in mind that childNodes only looks one level down, so the value will be the same so long as you have the same number of immediate children. Perhaps what you wanted was to count all the children all the way down the tree?
If you want all the child elements, you can use getElementsByTagName("*"). Note that the use of the "*" argument doesn't work in IE5.5, which could also pose trouble for more recent versions of IE when running in quirks mode.
Another good thing to know about childNodes.length is that it may return a different value in IE than other browsers because of different ways of counting text nodes . . . If you want to exclude text nodes and only count elements you can loop through the childNodes and check the value of nodeType (1 = ELEMENT_NODE, 2 = ATTRIBUTE_NODE, 3 = TEXT_NODE, etc.) You can also use children instead of childNodes if you only want element nodes, but in IE this incorrectly counts comment nodes, and in Firefox it wasn't supported until version 3.5.
Perhaps the child node is a DIV or Table that wraps up the nodes you need to count?
I'm trying to allow the user to edit a contenteditable div, but am finding that I can't use pasteHTML unless there is some text selected.
I thought document.selection.createRange() would return a valid zero-length selection (i.e. a position), but alas no.
I've really struggled to find any solution to this that doesn't involve iframes (not an option at present).
Any suggestions/ideas/questions most welcome.
Make sure the focus is on the editable div before creating the TextRange from the selection:
var div = document.getElementById("your_div");
div.focus();
document.selection.createRange().pasteHTML("<b>PASTED</b>");
You could use a form textarea. This is simple and cross-browser, but don't know to what extent your styling is limited or whether using a form or form element is appropriate for your issue.