Find the element with the most text - javascript

It's like this:
text = $('#content').find('.text'),
length = 0;
longest = '';
$(text).each(function(){
if($(this).text().length > length){
length = $(this).text().length;
longest = $(this);
}
});
alert($(longest).text());
But can I do it without the loop? I mean somehow directly select the longest text element? Because this is too slow

You cannot do it without a loop. There is no built-in DOM function to find the element with the longest text, thus some piece of code has to iterate through them and find which one is longest.
Here's a cleaned up version of your code:
var longestSoFar = -1;
var longestItem;
$('#content .text').each(function(){
var text = $(this).text();
if (text.length > longestSoFar) {
longestSoFar = text.length;
longestItem = this;
}
});
alert($(longestItem).text());
The only other thing I can think of would be to first compare heights of .text elements and find the ones that taller than the others and then compare text length in just those. This is probably a lot quicker than computing the actual length of the text, but whether it would be useful or not depends upon your layout and whether height would be an accurate filter or not.
To speed it up further, we'd probably have to see what your HTML generally looks like to have an idea of where the slow-down is coming from. .text() isn't a particularly fast operation because it has to walk all the text nodes in a given element and accumulate all their text. If there's only one node in the element, it's quick, but if there's lots of involved HTML in the node, then it has a lot of nodes and childnodes to walk to find all the text nodes.
Note, in your code you were making a jQuery object out of something that was already a jQuery object. It works that way, but it's a waste of a function call when it's already a jQuery object.

The plain script version is pretty similar:
var longestSoFar = -1;
var longestItem;
var els = document.querySelectorAll('#content .text');
for (var i=0, iLen=els.length, len; i<iLen; i++) {
len = (els[i].textContent || els[i].innerText).length;
if ( len > longestSoFar) {
longestItem = els[i];
longestSoFar = len;
}
}
alert(longestItem.textContent || longestItem.innerText);

Other way use get() to save it to Array and then sort the array with a custom sort using a function which uses length as way of sorting and not in alphabetical order, like:
var listitems = $(".text").get();
listitems.sort(function(a, b) {
var compA = $(a).text().length;
var compB = $(b).text().length;
return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;});
// Get the last element of the array which has the longest length
alert("Longest = "+$(listitems[listitems.length-1]).text());
See JSFiddle

fiddle DEMO
fiddle DEMO-1
var con = $('#content');
var arr = con.find('.text').map(function () {
return $(this).text().length;
}).get();
var longest = arr.indexOf(Math.max.apply(Math,arr));
alert($('#content .text:nth-child(' + ++longest + ')').text());

Related

Get text between 2 or more substrings

This is my first question, so excuse me if I mess something up. I'm new.
Anywho, recently I've been designing a function which takes a string and 2 substrings, and then returns an array of the positions of both substrings so I can later substring the actual string using the positions of the substrings I'm searching for. I hope that makes sense.
function m(s,s1,s2,prevTable){
var a = prevTable || []
if (s.indexOf(s1) > -1 && s.indexOf(s2, s.indexOf(s1)) > -1){
a.push([s.indexOf(s1),s.indexOf(s2, s.indexOf(s1))+s2.length])
s=s.substring(s.indexOf(s2, s.indexOf(s1)+s2.length))
console.log(s)
m(s,s1,s2,a);
}
return a;
}
So to summarize it makes an array (a), finds the position of s1 and s2 (plus it's own length so it includes s2) in the source string (s), adds them to the array as it's own array.
E.g a would be: a=[[2,5]], deletes up to where s2 was found (+s2.length to include s2), and then repeats it with the new string unless it can't find both s1 and s2, in which case it returns a.
However, it does not work as I intended it to.
Upon running this:
var s = "Hey. This is pointless. Middle is always neutral. This is not
pointless."
var a=m(s,"This","pointless.")
for (i=0;i<a.length;i++){
console.log(s.substring(a[i][0],a[i][1]))
}
The result I get is:
This is pointless.
dle is always neutral.
When I am expecting:
This is pointless.
This is not pointless.
Also, is there a name for this technique?
What you are trying to do could be accomplished more easily using regular expressions (MSDN Docs).
Here is a simple example, note: I threw this together quickly, it may not handle all input perfectly.
function splitBetweenTwoStrings(str, s1, s2){
var reg = new RegExp("("+s1+".*?"+s2+")", "g");
var result = [];
var r = null;
//get all instances and push into result array
while((r=reg.exec(str))){
result.push(r[1]);
}
return result;
}
console.log(splitBetweenTwoStrings("Hey. This is pointless. Middle is always neutral. This is not pointless.","This","pointless."))
You could do it by creating another method to check indices (I received help from (here)[https://stackoverflow.com/a/20968478/7535444]) and then loop over the occurrences.
var s = "Hey. This is pointless. Middle is always neutral. This is not pointless.";
var results = m(s, "This", "pointless.");
for (var i = 0; i < results.length; i++) {
console.log(results[i]);
}
function m(s, s1, s2) {
var s1Occurences = occurences(s, s1);
var s2Occurences = occurences(s, s2);
var loopCount = Math.min(s1Occurences.length, s2Occurences.length);
var results = [];
for (var i = 0; i < loopCount; i++) {
results.push(s.substring(s1Occurences[i], s2Occurences[i] + s2.length));
}
return results;
}
function occurences(main, sub) {
var indices = [];
for(var pos = main.indexOf(sub); pos !== -1; pos = s.indexOf(sub, pos + 1)) {
indices.push(pos);
}
return indices;
}
The problem with your code is, that in your example the second array element of "a" ([[5,23],[27,49]]) is relative to the temporary "s"-string created for the second call of m(). You would have to shift the values by the length of the cut-off-part of the "s"-string: [[5,23],[27+23,49+23]].
But I would recommend to use something like Will P.'s method.

How to write Javascript to search nodes - without getElementsByClassName

I'm very new at recursion, and have been tasked with writing getElementsByClassName in JavaScript without libraries or the DOM API.
There are two matching classes, one of which is in the body tag itself, the other is in a p tag.
The code I wrote isn't working, and there must be a better way to do this. Your insight would be greatly appreciated.
var elemByClass = function(className) {
var result = [];
var nodes = document.body; //<body> is a node w/className, it needs to check itself.
var childNodes = document.body.childNodes; //then there's a <p> w/className
var goFetchClass = function(nodes) {
for (var i = 0; i <= nodes; i++) { // check the parent
if (nodes.classList == className) {
result.push(i);
console.log(result);
}
for (var j = 0; j <= childNodes; j++) { // check the children
if (childNodes.classList == className) {
result.push(j);
console.log(result);
}
goFetchClass(nodes); // recursion for childNodes
}
goFetchClass(nodes); // recursion for nodes (body)
}
return result;
};
};
There are some errors, mostly logical, in your code, here's what it should have looked like
var elemByClass = function(className) {
var result = [];
var pattern = new RegExp("(^|\\s)" + className + "(\\s|$)");
(function goFetchClass(nodes) {
for (var i = 0; i < nodes.length; i++) {
if ( pattern.test(nodes[i].className) ) {
result.push(nodes[i]);
}
goFetchClass(nodes[i].children);
}
})([document.body]);
return result;
};
Note the use of a regex instead of classList, as it makes no sense to use classList which is IE10+ to polyfill getElementsByClassName
Firstly, you'd start with the body, and check it's className property.
Then you'd get the children, not the childNodes as the latter includes text-nodes and comments, which can't have classes.
To recursively call the function, you'd pass the children in, and do the same with them, check for a class, get the children of the children, and call the function again, until there are no more children.
Here are some reasons:
goFetchClass needs an initial call after you've defined it - for example, you need a return goFetchClass(nodes) statement at the end of elemByClass function
the line for (var i = 0; i <= nodes; i++) { will not enter the for loop - did you mean i <= nodes.length ?
nodes.classList will return an array of classNames, so a direct equality such as nodes.classList == className will not work. A contains method is better.
Lastly, you may want to reconsider having 2 for loops for the parent and children. Why not have 1 for loop and then call goFetchClass on the children? such as, goFetchClass(nodes[i])?
Hope this helps.

Inserting html elements while DOM is changing

My code should insert HTML content in all divs that have a predefined class name, without using jQuery and at least compatible with IE8 (so no getElementsbyClass).
The html:
<div class="target">1</div>
<div class="target">2</div>
<div class="target">3</div>
<div class="target">4</div>
The javascript:
var elems = document.getElementsByTagName('*'), i;
for (wwi in elems) {
if((' ' + elems[wwi].className + ' ').indexOf(' ' + "target" + ' ') > -1) {
elems[wwi].innerHTML = "YES";
//elems[wwi].innerHTML = "<div>YES!</div>";
}
}
You can try it here.
As you can see inside each div the word YES is printed. Well the if you comment elems[wwi].innerHTML = "YES"; and replace that for elems[wwi].innerHTML = "<div>YES!</div>" the code fails. I suppose is because inserting div elements modify the DOM and in consequence the FOR cycle fails. Am i right?
Well i can solve this pretty ugly by recalling the for cycle each time i make an innerHTML, and when i insert the code i can add a class (like data-codeAlreadyInserted=1) to ignore the next time the FOR pass in that div. But again, this is pretty much a very bad solution since for an average site with many tags I can even freeze the user browser.
What do you think? lets suppose i dont know the amount of tags i insert on each innerHTML call.
"I suppose is because inserting div elements modify the DOM and in consequence the FOR cycle fails. Am i right?"
Pretty much. Your elems list is a live list that is updated when the DOM changes. Because you're adding a new div on every iteration, the list keeps growing and so you never get to the end.
To avoid this, you can either do a reverse iteration,
for (var i = elems.length-1; i > -1; i--) {
// your code
}
or convert the list to an Array.
var arr = [];
for (var i = 0, len = list.length; i < len; i++) {
arr.push(elems[i]);
}
for (i = 0; i < len; i++) {
// your code
}
Another way is to use replaceChild instead of innerHTML. It works better and it's way faster:
var newEl = elem[wwi].cloneNode(false);
newEl.innerHTML = html;
elem[wwi].parentNode.replaceChild(newEl, elem[wwi]);
You can take a copy of the live node list:
var nodes = [];
for (var i = 0, n = elems.length; i < n; ++i) {
nodes.push(elems[i]);
}
and then use a proper for loop, not for ... in to iterate over the array:
for (var i = 0, n = nodes.length; i < n; ++i) {
...
}
for ... in should only be used on objects, not arrays.

Javascript getElementById based on a partial string

I need to get the ID of an element but the value is dynamic with only the beginning of it is the same always.
Heres a snippet of the code.
<form class="form-poll" id="poll-1225962377536" action="/cs/Satellite">
The ID always starts with poll- then the numbers are dynamic.
How can I get the ID using just JavaScript and not jQuery?
You can use the querySelector for that:
document.querySelector('[id^="poll-"]').id;
The selector means: get an element where the attribute [id] begins with the string "poll-".
^ matches the start
* matches any position
$ matches the end
jsfiddle
Try this.
function getElementsByIdStartsWith(container, selectorTag, prefix) {
var items = [];
var myPosts = document.getElementById(container).getElementsByTagName(selectorTag);
for (var i = 0; i < myPosts.length; i++) {
//omitting undefined null check for brevity
if (myPosts[i].id.lastIndexOf(prefix, 0) === 0) {
items.push(myPosts[i]);
}
}
return items;
}
Sample HTML Markup.
<div id="posts">
<div id="post-1">post 1</div>
<div id="post-12">post 12</div>
<div id="post-123">post 123</div>
<div id="pst-123">post 123</div>
</div>
Call it like
var postedOnes = getElementsByIdStartsWith("posts", "div", "post-");
Demo here: http://jsfiddle.net/naveen/P4cFu/
querySelectorAll with modern enumeration
polls = document.querySelectorAll('[id ^= "poll-"]');
Array.prototype.forEach.call(polls, callback);
function callback(element, iterator) {
console.log(iterator, element.id);
}
The first line selects all elements in which id starts ^= with the string poll-.
The second line evokes the enumeration and a callback function.
Given that what you want is to determine the full id of the element based upon just the prefix, you're going to have to do a search of the entire DOM (or at least, a search of an entire subtree if you know of some element that is always guaranteed to contain your target element). You can do this with something like:
function findChildWithIdLike(node, prefix) {
if (node && node.id && node.id.indexOf(prefix) == 0) {
//match found
return node;
}
//no match, check child nodes
for (var index = 0; index < node.childNodes.length; index++) {
var child = node.childNodes[index];
var childResult = findChildWithIdLike(child, prefix);
if (childResult) {
return childResult;
}
}
};
Here is an example: http://jsfiddle.net/xwqKh/
Be aware that dynamic element ids like the ones you are working with are typically used to guarantee uniqueness of element ids on a single page. Meaning that it is likely that there are multiple elements that share the same prefix. Probably you want to find them all.
If you want to find all of the elements that have a given prefix, instead of just the first one, you can use something like what is demonstrated here: http://jsfiddle.net/xwqKh/1/
I'm not entirely sure I know what you're asking about, but you can use string functions to create the actual ID that you're looking for.
var base = "common";
var num = 3;
var o = document.getElementById(base + num); // will find id="common3"
If you don't know the actual ID, then you can't look up the object with getElementById, you'd have to find it some other way (by class name, by tag type, by attribute, by parent, by child, etc...).
Now that you've finally given us some of the HTML, you could use this plain JS to find all form elements that have an ID that starts with "poll-":
// get a list of all form objects that have the right type of ID
function findPollForms() {
var list = getElementsByTagName("form");
var results = [];
for (var i = 0; i < list.length; i++) {
var id = list[i].id;
if (id && id.search(/^poll-/) != -1) {
results.push(list[i]);
}
}
return(results);
}
// return the ID of the first form object that has the right type of ID
function findFirstPollFormID() {
var list = getElementsByTagName("form");
var results = [];
for (var i = 0; i < list.length; i++) {
var id = list[i].id;
if (id && id.search(/^poll-/) != -1) {
return(id);
}
}
return(null);
}
You'll probably have to either give it a constant class and call getElementsByClassName, or maybe just use getElementsByTagName, and loop through your results, checking the name.
I'd suggest looking at your underlying problem and figure out a way where you can know the ID in advance.
Maybe if you posted a little more about why you're getting this, we could find a better alternative.
You use the id property to the get the id, then the substr method to remove the first part of it, then optionally parseInt to turn it into a number:
var id = theElement.id.substr(5);
or:
var id = parseInt(theElement.id.substr(5));
<form class="form-poll" id="poll-1225962377536" action="/cs/Satellite" target="_blank">
The ID always starts with 'post-' then the numbers are dynamic.
Please check your id names, "poll" and "post" are very different.
As already answered, you can use querySelector:
var selectors = '[id^="poll-"]';
element = document.querySelector(selectors).id;
but querySelector will not find "poll" if you keep querying for "post": '[id^="post-"]'
If you need last id, you can do that:
var id_list = document.querySelectorAll('[id^="image-"]')
var last_id = id_list.length
alert(last_id)

How do I avoid looping through an array to find a partial match?

I am looping through an array of english phrases, and if i find a match, with the current text node, i replace it with it's translation in the non_english array. All of that works 100% for exact matches.
But for partial matches, I need to use the .match command, which allows for partial matches.
My code to search for exact matches is like this:
var found = $.inArray(value,en_lang);
Then if there is a found value, then do replacement of text. This method is fast and I love it.
However to do partial word/phrase matching, I have to use this looping code.
// loop thru language arrays
for (var x = en_count; x > 0; x--) {
// assign current from/to variables for replace
var from = en_lang[x];
var to = other_lang[x];
// if value match do translation
if (value.match(from)) {
content(node, value.replace(from, to));
}
// mark this node as translated
if ($.browser.msie == 'false') {
$(node).data('translated', 'yes');
}
}
This does the job but is pretty slow. After a lot of research, I have found that I can convert the english array to a list-based string via the join command.
But I am unable to come up with a function to search this list for a partial match, and return the position in the list.
I was trying out this old js function created in 2006. But I can't figure out how to get the position back, correctly.
function listfind(list, value, delimiters) {
if (!delimiters) {
var delimiters = ','
}
_TempListSplitArray = list.split(delimiters)
var FoundIdx = 0;
for (i = 0; i < _TempListSplitArray.length; i++) {
if (_TempListSplitArray[i] == value) {
FoundIdx = i + 1;
break
}
if (value.match(_TempListSplitArray[i])) {
FoundIdx = i + 1;
break
}
}
return FoundIdx
}
Thank you for your time.
Javascript has a foreach type of system but its still based on a loop
var array = ['hello', 'world'];
for(var key in array){
alert(array[key]);
}
Thats the best your getting for looping though an array but this way allso works with objects
var obj = {'one':'hello', 'two':'world'];
for(var key in obj){
alert("key: "+key+" value: "+obj[key]);
}
UPDATED for Comments on your question
You can just replace the text you know
var str = "hello World";
str = str.replace("hello", "Bye bye");
alert(str);

Categories

Resources