Using javascript, I'd like to get the selected text within a div. My question is almost the same as Get selected text from only one div except that within the div there may be an arbitrary number of other nested divs. The answer to that question only works for one nested div.
In other words, say I have a div, STUFF. STUFF could be arbitrary HTML, including plenty of other nested divs. I want the selected HTML within the forkfork div, including any selected nested divs.
Updated: Here's some code, adapted from the previous question. I want to be able to select anything within the "forkfork" div, and have it returned, ideally the raw HTML.
<html>
<body>
<div id="forkfork">want to be able to select anthing within here
<div id="child">or in here</div>
<div id="child1">perhaps in here,
<div id="child2">maybe in here,
<div id="child3">or in here
</div>
</div>
</div>
though here.
</div>
<div id="b">don't want to have this returned.</div>
<button id="btn">btn
<script>
function select() {
var flag = 0;
sel = window.getSelection();
for (var i = 0; i < sel.rangeCount; i++) {
var s = sel.getRangeAt(i).startContainer.parentNode.id;
var e = sel.getRangeAt(i).endContainer.parentNode.id;
if (s == "forkfork") flag = 1;
if (flag = 1 && e == "forkfork" || e == "child") flag = 2;
}
if (flag == 2) alert(sel);
}
document.getElementById("btn").addEventListener("click", function () {
select();
});
</script>
</body>
And a jsfiddle: http://jsfiddle.net/4n3ud3nb/1/
Using cloneContents() from the range the contents of the selection could be returned.
The first cloneContents call will return the contents if there was an over run of our targeted element forkfork.
If we find no over run of the selection, we will check to make sure our selection starting range is a child of the targeted element.
Here is the example snippet:
function select(event) {
var sel;
//console.log(document.getSelection);
if (document.getSelection)
sel = document.getSelection();
else
sel = document.selection.createRange();
//console.log('sel',sel);
var selText = '';
var forkfork = document.getElementById("forkfork");
if (sel.rangeCount > 0) {
var range = sel.getRangeAt(0);
var clonedSelection = range.cloneContents().querySelector("#forkfork");
if (clonedSelection) {
selText = clonedSelection.innerHTML;
} else {
clonedSelection = range.cloneContents();
var startNode = sel.getRangeAt(0).startContainer.parentNode;
//console.log(isChild(startNode, forkfork));
if (isChild(startNode, forkfork)) {
var div = document.createElement('div');
div.appendChild(clonedSelection);
selText = div.innerHTML;
}
}
}
alert(selText.toString());
}
function isChild(child, parent) {
if (child === parent) return true;
var current = child;
while (current) {
if (current === parent) return true;
current = current.parentNode;
}
return false;
}
document.getElementById("btn").addEventListener("click", select);
#a {
background: #C0D9AF;
}
#b,#b2 {
background: #ECC8EC;
}
#c {
background: #AFEEEE;
}
<div id="b">don't want to have this returned.</div>
<div id="forkfork">want to be able to select anthing within here
<div id="child">or in here</div>
<div id="child1">perhaps in here,
<div id="child2">maybe <strong>in</strong> here,
<div id="child3">or in here</div>
</div>
</div>
though here.
</div>
<div id="b2">And, don't want to have this returned.</div>
<button id="btn">btn</button>
.innerHTML will return everything inside the div.
(function() {
$(document).ready(function() {
var x = document.getElementById('forkfork').innerHTML;
console.log(x);
});
}());
console.log(x) Will output this:
want to be able to select anthing within here
<div id="child">something else</div>
<div id="child1">another thing,
<div id="child2">and another thing,
<div id="child3">and another thing
</div>
</div>
</div>
though here.
If you want all text:
(function() {
$(document).ready(function() {
var x = $('#forkfork').text();
console.log(x);
});
}());
Which will output:
want to be able to select anthing within here
something else
another thing,
and another thing,
and another thing
though here
Related
Basic html:
<button id="changer">Changer button</button>
<div id="text"> </div>
"Changer" is the button element in html, "text" is the div tag in which our text will be placed.
var selector = 0;
We set the selector to 0. Next, every time the button "changer" is clicked, we add 1 to the selector var, unless it has reached its max value of 14, in which case we start over. And based on the selector value, we pick from the available text values.
document.getElementById("Changer").onclick = function () {
if (selector < 14) {
selector++;
}
else {
selector = 0;
selector++;
}
document.getElementById("text").innerHTML = text;
}
if (selector = 1 ){
text = "<p>this is text 1</p>";
if (selector = 2 ){
text = "<p>this is text 2</p>";
etc...
The problem is, the function upon being clicked jumps right to the last text value available. How do I fix this? Will add live example soon if needed.
Your are assigning the selector inside the if condition to a value.
if(selector = 1) {...
What you actually want to do is check if the selectors value is equal to something like so:
if(selector == 1) {...
But you do not need to repeat the check, you can simply do:
var selector = 0;
var btn = document.getElementById('changer');
var output = document.getElementById('text');
btn.addEventListener('click', function() {
if (selector < 14) {
selector++;
output.innerHTML = "<p>this is text " + selector + "</p>";
} else {
selector = 0;
output.innerHTML = "";
}
})
<button id="changer">Changer button</button>
<div id="text"> </div>
i`m working on a Greasmonkey Script, no i want to know how i can test a class for its content.
For example:
<div class="same">Some content</div>
<div class="same">Other content</div>
<div class="same">evenmorecontent</div>
<div class="same">yesthisisalsocontent</div>
<div class="same">youmaynotizedthat</div>
<div class="same">thecontentisdifferent</div>
<div class="same">the cake is yellow</div>
Now i create a array with this classes
var arr = document.getElementsByClassName('same');
Now i want to get the position of "evenmorecontent" in the Array! How do i do this?
If you wanted to use jQuery, you could do this in one line with the contains selector.
In straght JS you need to loop through the array:
var pos = -1;
for(var x=0;x<arr.length;arr++) {
if(arr[x].innerHTML.indexOf('evenmorecontent')>-1) {
pos = x;
}
}
If it occurs more than once, this will return the last position. It will return -1 if not found.
Try not to mix vanilla JS and jQuery while doing this. The following function returns the position of the first occurrence of the text:
var getTextPosition = function (selector, text) {
var pos = -1;
$(selector).each(function(index) {
if($(this).text() === text) {
pos = index;
return false;
}
});
return pos;
};
Example usage:
getTextPosition(".same", "evenmorecontent");
This isn't possible with just one selector. You'll have to loop:
var sameEl = (function() {
var sameArr = document.getElementsByClassName('same');
for (index in sameArr) {
if (sameArr[index].innerHTML === 'evenmorecontent') {
return sameArr[index];
}
}
})();
Is there a way to get innerText of only the top element (and ignore the child element's innerText) ?
Example:
<div>
top node text
<div> child node text </div>
</div>
How to get the "top node text" while ignoring "child node text" ? innerText property of top div seem to return concatenation of both inner , top text.
Just iterate over the child nodes and concatenate text nodes:
var el = document.getElementById("your_element_id"),
child = el.firstChild,
texts = [];
while (child) {
if (child.nodeType == 3) {
texts.push(child.data);
}
child = child.nextSibling;
}
var text = texts.join("");
This will work in your example:
document.getElementById("item").firstChild.nodeValue;
Note: Keep in mind that this will work if you know you are dealing with that specific HTML. If your HTML can change, for example to:
<div>
<div class="item"> child node text </div>
top node text
</div>
then you should use the more generic solution by #Tim Down
Here is working code snippet:
window.onload = function() {
var text = document.getElementById("item").firstChild.nodeValue;
document.getElementById("result").innerText = text.trim();
};
#result {
border: 1px solid red;
}
<div id="item">
top node text
<div> child node text </div>
</div>
<strong>Result:</strong> <div id="result"></div>
Clone the element.
Loop through all child nodes (backwards, to avoid conflicts):If the element has a tagName attribute, then it's an element: Remove the node.
Use innerText to get the textual contents (with fallback to textContent, when innerText is not supported).
Code:
var elem = document.getElementById('theelement');
elem = elem.cloneNode(true);
for (var i=elem.childNodes.length-1; i>=0; i--) {
if (elem.childNodes[i].tagName) elem.removeChild(elem.childNodes[i]);
}
var innerText = elem['innerText' in elem ? 'innerText' : 'textContent'];
function getDirectInnerText(element) {
var childNodes = element.childNodes;
result = '';
for (var i = 0; i < childNodes.length; i++) {
if(childNodes[i].nodeType == 3) {
result += childNodes[i].data;
}
}
return result;
}
element = document.querySelector("div#element");
console.log(getDirectInnerText(element))
<div id="element">
top node text
<div> child node text </div>
</div>
If you don't want to ignore the child element's inner text, use the following function:
function getInnerText(el) {
var x = [];
var child = el.firstChild;
while (child) {
if (child.nodeType == 3) {
x.push(child.nodeValue);
}
else if (child.nodeType == 1) {
var ii = getInnerText(child);
if (ii.length > 0) x.push(ii);
}
child = child.nextSibling;
}
return x.join(" ");
}
I have many DIVs, let's say 100, split by categories like that :
<div id="div1" class="cat01">text</div>
<div id="div2" class="cat02">text</div>
<div id="div3" class="cat01">text</div>
<div id="div4" class="cat02">text</div>
<div id="div5" class="cat03">text</div>
<div id="div6" class="cat01">text</div>
And I want to filter a specific class
Let's say I click on a button "filter only cat02"
I then have only this on the page :
<div id="div2" class="cat02">text</div>
<div id="div4" class="cat02">text</div>
I do not have to use a class to define the categories, but it seems the appropriate solution...
Thanks you VERY much for your help!
EDIT : much clearer
Here is the file :
<div id="principal">
<div class="abc1 categ1">Text0</div>
<div class="abc5 categ3">Text0</div>
<div class="abc4 categ2">Text1</div>
<div class="abc7 categ1">Text0</div>
<div class="abc2 categ2">Text2</div>
<div class="abc4 categ3">Text0</div>
<div class="abc6 categ1">Text0</div>
<div class="abc7 categ2">Text3</div>
</div>
and I want this :
Text1
Text2
Text3
You can do something like this:
var list = document.getElementsByClassName('cat02');
for (var i = 0; i < list.length; i++) {
// this will remove the node from the page
list[i].parentNode.removeChild(list[i]);
// if you just want to hide it, you can do this:
// list[i].style.display = 'none';
}
Note that getElementsByClassName isn't supported by most browsers -- for those that don't, you may have to use a custom implementation such as the one here.
Update: If all your DIVs are direct children of a single DIV and they each contain only one class, it makes the task much simpler. You can skip getElementsByClassName and instead just iterate over the children:
function showOnly(parent, className) {
className = ' ' + className + ' ';
var e = parent.firstChild;
while (e != null) {
if (e.nodeType == 1) {
if ((' ' + e.className + ' ').indexOf(className) > -1)
e.style.display = 'block';
else
e.style.display = 'none';
}
e = e.nextSibling;
}
}
showOnly(document.getElementById('masterdiv'), 'cat02');
Edit: There were a couple of errors previously that I've fixed now. The indexOf comparison should be > -1 instead of > 0 and also the list of children includes empty text nodes (spaces between tags) that should be ignored, hence the check for e.nodeType == 1.
Seem like jQuery would help a lot here. You could just call $("div[class!='class02']") and get an array of items you want. Then, you could call .addClass('hidden') or whatever you need to do to the other items.
A brute force solution:
var list = document.getElementsByTagName('div');
for (var i = 0; i < list.length; i++) {
if (list[i].className != 'cat02') {
list{i].style.display = 'hidden';
}
}
Wrap this in a function to something like this:
function filterDivs(nameToFilter) {
var list = document.getElementsByTagName('div');
for (var i = 0; i < list.length; i++) {
if (!contains(list[i].className.split(' '), nameToFilter)) {
list{i].style.display = 'hidden';
}
}
}
EDIT:
using this function to search for strings in an array that I found here
function contains(a, obj) {
var i = a.length;
while (i--) {
if (a[i] === obj) {
return true;
}
}
return false;
}
With only 100 divs, it should run pretty quickly, but if you increase this amount a lot, it will become slow.
I also recommend that you don't remove items from a collection while iterating through it, that will cause you trouble. Hide them instead, or work with different collections.
I have some text below called (16 Courses). I need to hide only this text, but I can't seem to hide it no matter what I try using jquery. Is there any help someone could provide so I can hide on this text?
<div id="programAttributes">
<div class="left" id="credits">
<h3>Credits</h3>
<h3 class="cost">48</h3>
(16 Courses)
</div>
<div class="gutter12 left"> </div>
<div class="left" id="costPer">
<h3>Cost Per Credit</h3>
<h3 class="cost">$300</h3>
</div>
</div>
I thought if I could write something like this that would do the trick, but I am so far unsuccessful.
$("#credits:not([class!=h3])").hide();
Usage
// hides in the whole document
hideText("(16 Courses)");
// only hide inside a specific element
hideText("(16 Courses)", $('#programAttributes'));
// make it visible again
showText("(16 Courses)");
[See it in action]
CSS
.hiddenText { display:none; }
Javascript
// escape by Colin Snover
RegExp.escape = function(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}
function hideText(term, base) {
base = base || document.body;
var re = new RegExp("(" + RegExp.escape(term) + ")", "gi");
var replacement = "<span class='hiddenText'>" + term + "</span>";
$("*", base).contents().each( function(i, el) {
if (el.nodeType === 3) {
var data = el.data || el.textContent || el.innerText;
if (data = data.replace(re, replacement)) {
var wrapper = $("<span>").html(data);
$(el).before(wrapper.contents()).remove();
}
}
});
}
function showText(term, base) {
var text = document.createTextNode(term);
$('span.hiddenText', base).each(function () {
this.parentNode.replaceChild(text.cloneNode(false), this);
});
}
You can check for and remove textnodes like this:
$("#credits").contents().filter(function() {
if(this.nodeType == 3)
this.parentNode.removeChild(this);
});
You can test it here, this gets all the nodes (including text nodes) with .contents(), then we loop through, if it's a text node (.nodeType == 3) then we remove it.
Could you wrap it in a separate span, and then do:
$('#credits span').hide();
?
Try wrapping the text in a span as follows:
<div class="left" id="credits">
<h3>Credits</h3>
<h3 class="cost">48</h3>
<span id="toHide">(16 Courses)</span>
</div>
then you can use jquery:
$("#credits > span)").hide();
the hide() function has to be applied to a DOM element.
I would use a label tag around the text so I can handle it with jquery.
It's textnode. Loop thru all parents nodes and if it's type is textnode, hide it. See also this:
How do I select text nodes with jQuery?