arithmetics on selected DOM element text - javascript

I've got a simple jQuery $("span.value") which select nodes containing text. This text is guaranteed to be an integer. How do I calculate the sum of all selected node's text, and place the sum into another node?
<span class="value">3</span>
<span class="value">4</span>
<span class="value">5</span>
<span class="sum">?</span>

You could do this:
var sum = 0;
$('.value').each(function(index, el) {
sum += parseInt($(this).text());
});
$('.sum').text(sum); //careful - will set the text for *all* elements with class = sum
I'd suggest using an id instead of a class for the sum <span>, even if you're sure there's going to be just one. IMO, it's be less error-prone..

Related

How to get column number based on text

I've divided the Html content (which belongs to an eBook) into multiple columns using the following steps.
1) I've added the HTML inside the content which is inside a container.
<body>
<div id="container">
<div id="content">
BOOK HTML CONTENT
<span id="endMarker"></span>
</div>
</div>
</body>
2) Next, I've added the CSS style of the content and the container as shown below:
#container {
width: 240px;
overflow: hidden;
background-color: yellow;
}
#content {
position: relative;
height: 30em;
-moz-column-width: 240px;
-webkit-column-width: 240px;
column-width: 240px;
-moz-column-gap: 10px;
-webkit-column-gap: 10px;
column-gap: 10px;
}
Now, I want to find the column number of the text (or a line) using javascript?
There are other questions on SO that show how to get the column number based on the id. In my case, there are no id's. The only thing available is the text (or line) and I need to get the column number by searching through the Html content.
Currently, I've two "solutions" to get the column number but they are incomplete.
1) I can find whether the text exists or not by using window.find(text) after that I'm not sure what I've to do.
2) Another option is to add <span> with an id to every line temporarily and remove it. Once added, I can get the total column count up to that line (like shown below).
columnCount = Math.floor($('#marker').position().left/(columnWidth + columnGap));
This will give a wrong number if the line is extended to another column.
The second solution is tricky and book content is huge. I don't think this is the best way to get the column number. I'm looking for a simpler solution.
Try this, made a workable version for your question.
jsfiddle link
Although OP didn't tag question with jQuery, but actually used jQuery inside the question, I use it too for cleaner code. (and fit the question)
What I do in this example:
Make a long content cross several pages to visualize paging (with css: column-width).
Click on previous / next to browse pages.
Input and click 'find' button, make found texts highlighted.
List all columns (pages) found with input text.
Click on the link and jump to column with searched text.
In detail I made temporary DOM elements to calculate column, and remove them right after to keep DOM tree clean.
2017/6/1 Edited: Added highlight color for searched text.
$('#find').click( () => {
var text = $('#findtext').val();
var columns = [];
var doms = [];
while (window.find(text, false)) {
var sel = window.getSelection();
if (sel) {
var range = sel.getRangeAt(0);
var el = document.createElement("span");
var frag = document.createDocumentFragment();
frag.appendChild(el);
range.insertNode(frag);
columns.push(Math.floor(el.offsetLeft/(_columnWidth + _columnGap)));
doms.push(el);
}
}
/// distinct
columns = columns.filter( (value, index, self) => self.indexOf(value) === index );
/// show result
$("#foundlist").empty();
for (var i=0; i<columns.length; i++)
$("#foundlist").append(`<li>Found in Column ${columns[i]+1}</li>`);
/// remove dom. keep dom tree clean
while (doms.length > 0) {
var dom = doms.pop();
dom.parentNode.removeChild(dom);
}
});
Instead of adding a <span> beforehand, you could temporarily insert it at the point where you find your text and remove it again as soon as you have identified the position.
The key is how to find text in a long document. The interface for this task is the TreeWalker, that can iterate through every text node in a DOM subtree.
How to insert an element in the middle of a text node was copied from here.
Your question did not state it, but used jQuery as a dependency. This solution is using only the vanilla DOM interfaces.
var columnWidth = 240,
columnGap = 10;
function getColumn(text) {
// the subtree to search in
var root = document.getElementById('content');
// define an iterator that only searches in text nodes
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
// filter the text nodes to those containing the search text
acceptNode: function(node) {
if ( node.data.includes(text) ) {
return NodeFilter.FILTER_ACCEPT;
}
}
});
// look if there is a result
if (treeWalker.nextNode()) {
var node = treeWalker.currentNode;
// get start index of found text
var index = node.data.indexOf(text);
// and split into two nodes, referencing the second part
var foundTextNode = node.splitText(index);
// define an empty inline element
var span = document.createElement('span');
// insert it between the two text nodes
var elem = node.parentElement;
elem.insertBefore(span, foundTextNode);
// compute the column from the position of the element
// you might have to account for margins here
var x = span.getBoundingClientRect().left - root.getBoundingClientRect().left;
var column = Math.floor(x / (columnWidth + columnGap));
// restore previous state
elem.removeChild(span);
elem.normalize();
return column;
} else {
// no result
return false;
}
}
The obvious limitation to this solution is that it will not find text that is spanning multiple nodes. So, if you have a text fragment
<p>The quick brown fox <em>jumps</em> over the lazy dog.</p>
a search for 'fox jumps over' will not find this sentence.

Show/Hide Elements with multiple attributes by attribute selection (Javascript)

I try to find an easy solution (I am a totally coding beginner, just use javascript in widgets of a "out of the box" page) for the following problem:
There are multiple attributes visitor can select by click Remove/Show
attribute a (Remove/Show)
attribute b (Remove/Show)
attribute c (Remove/Show)
a.s.o.
based on visitors "selection", I would like to show or hide the list of elements:
element 1 (attribute a and b) - Remove if "a" OR "b" has been selected
element 2 (attribute a) - remove if "a" has been selected
element 3 (attribute a and c) - remove, if "a" OR "c" has been selected
a.s.o.
I am able already to hide elements based on a "selection", but in my solution every element show and hide only based on the unique ID (and so also only on the single selection).
The Javascript I found for that is:
<script type="text/javascript">
//<![CDATA[
function swap(openlink,closelink, linkid, dataid)
{
if( document.getElementById(dataid).style.display == 'none')
{
document.getElementById(dataid).style.display='inline';
document.getElementById(linkid).firstChild.nodeValue=closelink;
} else
{
document.getElementById(dataid).style.display='none';
document.getElementById(linkid).firstChild.nodeValue=openlink;
}
}
//]]>
</script>
And than I could use this HTML Code to Remove/Show the elements:
attribute a Remove
attribute b Remove
attribute c Remove
And my element will be Remove/Show by this:
<div id="showmeA" style="display:inline">Element 1</div>
<div id="showmeB" style="display:inline">Element 2</div>
<div id="showmeB" style="display:inline">Element 3</div>
Is there an easy way to add 2 ids to one "element", so that for example Element 1 could be hidden by id=showmeA AND id=showmeB?
You said the issue yourself: IDs are unique.
This is exactly why you should use something else than id, and class attribute is perfectly fine as it does not have to be unique.
Then, this means that the function will not look for your elements using getElementById() but getElementsByClassName().
Note that this function get elements, this involves that you have to loop through these elements and hide / show the ones targeted.
function swap(openlink, closelink, linkid, dataclass) {
var elements = document.getElementsByClassName(dataclass);
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if(element.style.display == 'none') {
element.style.display='inline';
document.getElementById(linkid).firstChild.nodeValue=closelink;
} else {
element.style.display='none';
document.getElementById(linkid).firstChild.nodeValue=openlink;
}
}
}
Do not forget to replace id by class attributes to your HTML, you can put in as much as you want, they simply must be separated by a space:
<div class="showmeA showmeB">Element 1</div>
<div class="showmeB">Element 2</div>
<div class="showmeC">Element 3</div>
Here is an example that you can use to better understand the function and attributes used in your solution, this solves your issue: https://jsfiddle.net/sy2mxscf/
It is also important to inform you that inline Javascript is bad, you should reconsider your code when your Javascript skill will increase.
In order to solve the issue pointed out in the comments, you have to use some kind of counter and increment it when you hide the element, decrement it when you show element of one of his class, and displaying the associate element when this counter is 0.
This is also why you need two differentiated links: the "Remove" to increment, and the "Show" to decrement.
There are several way to implement this solution:
Use an associative array in Javascript
Use a custom attribute on the element
Add and remove specific classes
I chose the last one but this may be not the best one, this is just one of the possibilities.
https://jsfiddle.net/sy2mxscf/2/
The idea is to add or remove a custom "hidemeX" class. If you click on two different "Remove" links targeting the same element, two classes will be added. If you then click on any "Show" link, the associate class will be removed. But there is still a "hidemeX" class remaining until you click on the second link, so the element is not displayed thanks to CSS.
As Delgan says, its better to use class here, and you can use those <a>'s id as their class, so when you use your function swap, you can easily trace back to decide if the elements is selected, so the div should be removed.
Below is how you can separate javascript logic and html structure.
var swap = function(e) {
var close = 'Remove', open = 'Show';
var next = this.text === close ? open : close;
// Set the clicked <a>'s text.
this.text = next;
// Get divs that will affect by this <a>
var affectTarget = this.id;
// Affected div elements
var targets = document.getElementsByClassName(affectTarget);
var i, len = targets.length;
var visible;
var watch, wLen, j;
// For each <div> that will be affect by the clicked <a>, we have to chec :
for (i = 0; i < len; ++i) {
// Get the classes that used as a list to show which <a> will have a effect on it.
watch = targets[i].classList;
wLen = watch.length;
// visibilty is default to inline, if no <a> that it watches is selected, then it'll show
visible = "inline";
for (j = 0; j < wLen; ++j) {
// If any of the <a> it watches is selected, set the visibilty to none.
if (document.getElementById(watch[j]).text === open) {
visible = "none";
break;
}
}
targets[i].style.display = visible;
}
};
// For each switcher, we register a click event for it.
var switchers = document.querySelectorAll("a.showSwitcher");
var i, len = switchers.length;
for (i = 0; i < len; ++i) {
switchers[i].addEventListener('click', swap);
}
attribute a Remove
attribute b Remove
attribute c Remove
<hr/>
<div class="swaplinkA swaplinkB" style="display:inline">Element 1</div>
<div class="swaplinkA"style="display:inline">Element 2</div>
<div class="swaplinkA swaplinkC"style="display:inline">Element 3</div>

Finding text length within a divs children

So i have an array of divs. each div contains three span elements. Each span element can contain any number of digits.
This is what i have so far:
var arrayOfDivs = $(".avgMaxClass");
$.each(arrayOfDivs, function (index, value) {
});
what i want to do is calculate the total length of all the text within all the spans within the div im iterating through. I can't figure it how to do that.
EDIT:
To be clear i don't want the total text length of all the divs. I want the text length of the current div that im iterating through
$(".avgMaxClass span").text().length;
JSFiddle demo
Edit:
As you stated in your comment that you want to know the length for each DIV, here is another way using jQuery.each():
var avgMaxClass = $('.avgMaxClass');
$.each(avgMaxClass, function(i,item) {
// Output span text length for each item
console.log($("span", item).text().length);
});
JSFiddle demo
Just iterate over the div and find its length.
$(".avgMaxClass").each(function(){
var thisDivsLength = $(this).find('span').text().length;
alert("Current div's length: " + thisDivsLength);
});
$.each is for non-jQuery objects. Use $.fn.each instead.
DEMO
You can use a selector to get the divs and spans at the same time, or to split the logic and show both parts individually, you can use two selectors and loop over the results of each.
var total = 0;
$(".avgMaxClass").each(function(index, value) {
$(value).find("span").each(function(cindex, child) {
total += $(child).text().length;
});
});
$("#result").text("Total length: " + total);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="avgMaxClass">
<span>1</span>
<span>12</span>
<span>123</span>
</div>
<div class="avgMaxClass">
<span>1234</span>
<pre>12345</pre>
<span>12345</span>
</div>
<div class="avgMaxClass">
<span>123456</span>
<span>1234567</span>
<span>12345678</span>
</div>
<div id="result"></div>

Calculate total of input text fields on a datatable in jsf

I have a JSF <h:datatable> with two columns.
column 1 : <h:outputText>, gets populated from bean data.
coulmn 2 : <h:inputText> boxes.
There is a "Total" field outside the table and I want to have it show the total of fields as entered in column2 in realtime. So I did searching around and found out that I need a JavaScript to do this. I am however quite new to JS.
Where I am confused is how to access the value of the input text box. What I have done so far:
function totalFrom() {
var element = document.getElementById('transferFundsForm:fundsFromTable:0:from_transferAmt');
if(element != null){
document.forms['transferFundsForm']['transferFundsForm:totalFrom'].value = document.forms['transferFundsForm']['transferFundsForm:totalFrom'].value+ element;
}
}
As far as I understand, the transferFundsForm:fundsFromTable:0, here 0 represents the first row. How do I refer to the element in column that is being edited?
I have called this function on onblur event of the textBox in column.
Also I read that I can use <f:ajax> for this as well, but I am using JSP instead of Facelets, so I can't use <f:ajax>.
The HTML DOM element representation of <table> element has a rows property which gives you an array of all <tr> elements. The HTML DOM representation of this <tr> element has a cells property which gives you an array of all <td> elements.
So, provided that the 2nd column of the table contains only one <input> element which holds the value you'd like to sum up, and that totalFrom is an <input> element (at least, you're attempting to set the value property and not innerHTML), you could achieve this as follows:
function totalFrom() {
var table = document.getElementById('transferFundsForm:fundsFromTable');
var total = 0;
for (var i = 0; i < table.rows.length; i++) {
var secondColumn = table.rows[i].cells[1];
var input = secondColumn.getElementsByTagName('input')[0];
var value = parseInt(input.value);
if (!isNaN(value)) {
total += value;
}
}
document.getElementById('transferFundsForm:totalFrom').value = total;
}
If the totalFrom is however a <h:outputText id="totalFrom">, then set it as follows instead:
document.getElementById('transferFundsForm:totalFrom').innerHTML = total;
Why don't you use the jsf server side event or user
http://livedemo.exadel.com/richfaces-demo/
it also provide you builtin ajax functionality

Javascript - get text between <li> by ID

I'm trying to write some javascript that will grab the inner text of li elements (1-16) and put them into hidden fields.
var myValue9 = document.getElementById("fileName9").value;
oForm.elements["fileName9"].value = myValue9;
<input name="fileName9" type="hidden" id="fileName9" />
<li id="wavName9"> Some Text </li>
How do I return the text in between the <li> and put into the hidden field?
Simple JavaScript:
document.getElementById("fileName9").value = document.getElementById("wavName9").innerText;
You could, in this case, also use innerHTML but that would also give you the HTML the element contains.
LI tags don't have a .value property. Using plain javascript, you could do it this way:
oForm.elements["fileName9"].value = document.getElementById("wavName9").innerHTML;
Or, to do all of them from 1 to 16, you could use this loop:
for (var i = 1; i <= 16; i++) {
oForm.elements["fileName" + i].value = document.getElementById("wavName" + i).innerHTML;
}
Or since you also tagged your post for jQuery, using jQuery you could do it like this:
$("#fileName9").val($("#wavName9").text());
Or, to do all of them from 1 to 16:
for (var i = 1; i <= 16; i++) {
$("#fileName" + i).val($("#wavName" + i).text());
}
Use jQuery to do it.
var myvar = $("#wavName9").html()
I think this will do in for all li's
$("li[id^=wavName]").each(function(){
var $this = $(this);
$this.closest("input[id^=fileName]").val($this.text())
});
Create your li's with id's following such a structure: listitem-n, where n is 1-16 and input fields following the same structure hiddeninputs-n (n = 1-16)
using jfriend00's code, add it in a loop that will traverse 16 times, incrementing a count variable that you will use to transfer the data from list items to hidden inputs
var count = 0;
for( i=0; i < 16; i++){
count ++;
$("form #hiddeninput-"+count).val($("#listitem-"+count).text());
}
Better validate the code, but there's the general idea.
You could also create the hidden fields in javascript from scratch, which would make the code abit more stable IMO as there's less chances of a hidden field missing in the form when the js is executed.
Using jQuery:
$('#fileName9').val($('#wavName9').text());
Note that you can change .text() to .html() to return the HTML structure rather than just the text.
You could automate this for multiple <li>'s like so:
$('li[id^="wavName"]').each(function () {
var number = this.id.replace('waveName', '');
$('#fileName' + number).val($(this).text());
});
This selects all <li>'s who's id starts with "wavName" and stores the text within the <li> tag in the hidden input who's id starts with "fileName" and ends with the same integer as the <li> tag.

Categories

Resources