r = r.replace(/<TR><TD><\/TD><\/TR>/gi, rider_html);
...does not work in IE but works in all other browsers.
Any ideas or alternatives?
I've come to the conclusion that the variable r must not have the value in it you expect because the regex replacement should work fine if there is actually a match. You can see in this jsFiddle that the replace works fine if "r" actually has a match in it.
This is the code from fiddle and it shows the proper replacement in IE.
var r = "aa<TR><TD></TD></TR>bb";
var rider_html = " foo ";
r = r.replace(/<TR><TD><\/TD><\/TR>/gi, rider_html);
alert(r);
So, we can't really go further to diagnose without knowing what the value of "r" is and where it came from or knowing something more specific about the version of IE that you're running in (in which case you can just try the fiddle in that version yourself).
If r came from the HTML of the document, then string matching on it is a bad thing because IE does not keep the original HTML around. Instead it reconstitutes it when needed from the parsed page and it puts some things in different order (like attributes), different or no quotes around attributes, different capitalization, different spacing, etc...
You could do something like this:
var rows = document.getElementsByTagName('tr');
for (var i = 0; i < rows.length; i++) {
var children = rows[i].children;
if (children.length === 1 && children[0].nodeName.toLowerCase() === 'td') {
children[0].innerHTML = someHTMLdata
}
}
Note that this sets the value of the table cell, rather than replacing the whole row. If you want to do something other than this, you'll have to use DOM methods rather than innerHTML and specify exactly what you actually want.
Related
Just started using indexOf() and lastIndexOf() functions and I know why they are used, however, the result doesn't make me feel happy :)
let str = $('#info').html();
// WORKS
//alert(str.lastIndexOf('√'));
// DOESN'T WORK
alert(str.lastIndexOf('√'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="info">√</div>
The problem is I get the alert result as "-1", which means the √ couldn't be found in the str variable. Using simple symbol √ it works, however, I'm not sure if it's a good practice using this symbol here.
In my opinion, another approach about this problem would be encoding √ symbol in the HTML to √, so using "Inspect element" feature you would see √.
What do you think?
There is no direct way to achieve this. But if you still want to do this way then you simply need to create a HEX value of the ASCII value:
let str = ascii_to_hexa($('#info').html());
str = '�'+str.toUpperCase()+';';
alert(str.lastIndexOf('√'));
function ascii_to_hexa(str)
{
var arr1 = [];
for (var n = 0, l = str.length; n < l; n ++){
var hex = Number(str.charCodeAt(n)).toString(16);
arr1.push(hex);
}
return arr1.join('');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="info">√</div>
When the browser reads and parses your HTML, it builds up a DOM, without retaining the exact HTML you provided. Later, if you ask for HTML, it builds a new HTML string using its own rules for doing that.
That's why str.lastIndexOf('√') doesn't work: The browser isn't under any obligation to give you back the character in the same form you used when you supplied it. It could give it back as just a character (√) or a named character reference (√ in this case) or a decimal numeric character reference (√), rather than the hex numeric character reference you're looking for.
You'll have to test on your target browsers to see what they give you, and then look for that. I suspect most if not all will return the actual character, and so your str.lastIndexOf('√') (or str.lastIndexOf('\u221A')) will be the way to go.
<div>√</div>
i'm implementing a charcounter in the UI, so a user can see how many characters are left for input.
To count, i use this simple function:
function typerCount(source, layerID)
{
outPanel = GetElementByID(layerID);
outPanel.innerHTML = source.value.length.toString();
}
source contains the field which values we want to meassure
layerID contains the element ID of the object we want to put the result in (a span or div)
outPanel is just a temporary var
If i activate this function, while typing the machine really slows down and i can see that FF is using one core at 100%. you can't write fluently because it hangs after each block of few letters.
The problem, it seems, may be the value.length() function call in the second line?
Regards
I can't tell you why it's that slow, there's just not enough code in your example to determine that. If you want to count characters in a textarea and limit input to n characters, check this jsfiddle. It's fast enough to type without obstruction.
It could be having problems with outPanel. Every time you call that function, it will look up that DOM node. If you are targeting the same DOM node, that's very expensive for the browser if it's doing that every single time you type a character.
Also, this is too verbose:
source.value.length.toString();
This is sufficient:
source.value.length;
JavaScript is dynamic. It doesn't need the conversion to a string.
I doubt your problem is with the use of innerHTML or getElementById().
I would try to isolate the problem by removing parts of the function and seeing how the cpu is used. For instance, try it all these ways:
var len;
function typerCount(source, layerID)
{
len = source.value.length;
}
function typerCount(source, layerID)
{
len = source.value.length.toString();
}
function typerCount(source, layerID)
{
outPanel = GetElementByID(layerID);
outPanel.innerHTML = "test";
}
As artyom.stv mentioned in the comments, cache the result of your GetElementByID call. Also, as a side note, what is GetElementByID doing? Is it doing anything else other than calling document.getElementById?
How would you cache this you say?
var outPanelsById = {};
function getOutPanelById(id) {
var panel = outPanelsById[id];
if (!panel) {
panel = document.getElementById(id);
outPanelsById[id] = panel;
}
return panel;
};
function typerCount(source, layerId) {
var panel = getOutPanelById(layerId);
panel.innerHTML = source.value.length.toString();
};
I'm thinking there has to be something else going on though, as even getElementById calls are extremely fast in FF.
Also, what is "source"? Is it a DOMElement? Or is it something else?
I'm receiving XML data via an AJAX call. One of the tags has a large amount of text, roughly 4000-5000 characters. In Firefox, the field is being truncated around the 3000th character. Most everything I've found online says there is no limit to node value sizes, but sometime it's implementation dependent - no solid answers.
Does anyone have any suggestions for why this might be occurring, assuming there is no restriction on the size of the nodeValue? Any workarounds if so?
<test>
<foo>very long string...</foo>
</test>
value = testTag.getElementsByTagName("foo").item(0).firstChild.nodeValue;
value is truncated.
-If I print the xmlHttp.responseText, all of the data from is printed.
Check this. It says:
"Also important to note is that although the specifications say that no matter how much text exists between tags, it should all be in one text node, in practice this is not always the case. In Opera 7-9.2x and Mozilla/Netscape 6+, if the text is larger than a specific maximum size, it is split into multiple text nodes. These text nodes will be next to each other in the childNodes collection of the parent element."
#Kooilnc has it right, 4k limit on text nodes in Firefox.
You can work around it by doing this:
function getNodeText(xmlNode) {
if(!xmlNode) return '';
if(typeof(xmlNode.textContent) != "undefined") return xmlNode.textContent;
return xmlNode.firstChild.nodeValue;
}
text = getNodeText(document.getElementsByTagName("div").item(0));
alert(text.length);
See it in action here: http://jsfiddle.net/Bkemk/2/
Function borrowed from here: http://www.quirksmode.org/dom/tests/textnodesize.html
What I've come up with instead of targeting a single node:
function getDataOfImmediateChild(parentTag, subTagName)
{
var val = "";
var listOfChildTextNodes;
var directChildren = parentTag.childNodes;
for (m=0; m < directChildren.length; m++)
{
if (directChildren[m].nodeName == subTagName)
{
/* Found the tag, extract its text value */
listOfChildTextNodes = directChildren[m].childNodes;
for (n=0; n < listOfChildTextNodes.length; n++)
{
if (typeof listOfChildTextNodes[n] == "TextNode")
val += listOfChildTextNodes[n].nodeValue;
}
}
}
return val;
It might be worthwhile to also ensure the listOfChildTextNodes[n] element is a TextNode.
#Ryley
The only reason I do an iteration over the direct children is because getElementsByTagName and getElementsById will return nodes that are farther down the hierarchy. Better explained as an example:
<foo>
<bar>
<zoo>
<bar>-</bar>
</zoo>
<bar></bar>
</zoo>
If I say fooTag.getElementsByTagName("bar"), it's going to return an array of both s, even though I only want the second (since it's the only true child of ). The only way I can think of enforcing this "search only my direct children" is by iterating over the children.
A short while back I asked a question here about how I could calculate when a heading was longer than one line within a given container, and subsequently wrap each of these lines in a <span>:
Use Javascript/jQuery to determine where a heading breaks to the next line?
I chose an answer which worked great for me, at least until I checked in IE7 and IE6, in which all the headings handled by this script rendered as
"undefinedundefinedundefinedundefinedundefinedundefined[...]"
on the page. As I'm not really a JavaScript person (that's why I asked such a question in the first place), it's really tough for me to figure out where the problem is. I assumed an undefined variable or something, but I just can't seem to grasp it.
Can anyone help?
I'll repeat the code here, but please refer to the link above for context:
$(function(){
$h = $('.fixed').find('h3');
$h.each(function(i,e){
var txt = $(e).text();
$th = $('<h3 />').prependTo($(e).parent());
var lh = $(e).text('X').height();
$(e).text('');
while (txt.length > 0) {
$th.text($th.text() + txt[0]);
txt = txt.slice(1);
if (($th.height() > lh) || (txt.length <= 0)) {
var shc = $th.text().split(' ');
var ph = shc.slice(0,-1).join(' ')+' ';
if (txt.length <= 0) { ph += shc.pop(); }
$('<span />').text(ph).appendTo($(e));
$th.text(shc.pop());
}
}
$th.remove();
})
});
You need to change
$th.text($th.text() + txt[0]);
to be
$th.text($th.text() + txt.charAt(0));
IE<8 doesn't accept string positions through array indexes ;)
The styling doesn't work, but that'll be a CSS issue which I couldn't fix before leaving. But everything is wrapped in spans :)
Nothing jumps out at me. But, since you mentioned in your comment to your question that you see "undefined" in Firebug, I would start there. Even though those browsers are failing gracefully, the fact that you see undefined there is your first hint to finding the problem for the harder-to-diagnose IE6/7. I would use Firebug and either breakpoint in the function, or use some console.log() calls to document what the values that you are working with are each step of the way. Once you start seeing undefined... you have likely found your problem.
how can i search an html page for a word fast?
and how can i get the html tag that the word is in? (so i can work with the entire tag)
To find the element that word exists in, you'd have to traverse the entire tree looking in just the text nodes, applying the same test as above. Once you find the word in a text node, return the parent of that node.
var word = "foo",
queue = [document.body],
curr
;
while (curr = queue.pop()) {
if (!curr.textContent.match(word)) continue;
for (var i = 0; i < curr.childNodes.length; ++i) {
switch (curr.childNodes[i].nodeType) {
case Node.TEXT_NODE : // 3
if (curr.childNodes[i].textContent.match(word)) {
console.log("Found!");
console.log(curr);
// you might want to end your search here.
}
break;
case Node.ELEMENT_NODE : // 1
queue.push(curr.childNodes[i]);
break;
}
}
}
this works in Firefox, no promises for IE.
What it does is start with the body element and check to see if the word exists inside that element. If it doesn't, then that's it, and the search stops there. If it is in the body element, then it loops through all the immediate children of the body. If it finds a text node, then see if the word is in that text node. If it finds an element, then push that into the queue. Keep on going until you've either found the word or there's no more elements to search.
You can iterate through DOM elements, looking for a substring within them. Neither fast nor elegant, but for small HTML might work well enough.
I'd try something recursive, like: (code not tested)
findText(node, text) {
if(node.childNodes.length==0) {//leaf node
if(node.textContent.indexOf(text)== -1) return [];
return [node];
}
var matchingNodes = new Array();
for(child in node.childNodes) {
matchingNodes.concat(findText(child, text));
}
return matchingNodes;
}
You can try using XPath, it's fast and accurate
http://www.w3schools.com/Xpath/xpath_examples.asp
Also if XPath is a bit more complicated, then you can try any javascript library like jQuery that hides the boilerplate code and makes it easier to express about what you want found.
Also, as from IE8 and the next Firefox 3.5 , there is also Selectors API implemented. All you need to do is use CSS to express what to search for.
You can probably read the body of the document tree and perform simple string tests on it fast enough without having to go far beyond that - it depends a bit on the HTML you are working with, though - how much control do you have over the pages? If you are working within a site you control, you can probably focus your search on the parts of the page likely to be different page from page, if you are working with other people's pages you've got a tougher job on your hands simply because you don't necessarily know what content you need to test against.
Again, if you are going to search the same page multiple times and your data set is large it may be worth creating some kind of index in memory, whereas if you are only going to search for a few words or use smaller documents its probably not worth the time and complexity to build that.
Probably the best thing to do is to get some sample documents that you feel will be representative and just do a whole lot of prototyping based around the approaches people have offered here.
form.addEventListener("submit", (e) => {
e.preventDefault();
var keyword = document.getElementById("search_input");
let words = keyword.value;
var word = words,
queue = [document.body],
curr;
while (curr = queue.pop()) {
if (!curr.textContent.toUpperCase().match(word.toUpperCase())) continue;
for (var i = 0; i < curr.childNodes.length; ++i) {
switch (curr.childNodes[i].nodeType) {
case Node.TEXT_NODE: // 3
if (curr.childNodes[i].textContent.toUpperCase().match(word.toUpperCase())) {
console.log("Found!");
console.log(curr);
curr.scrollIntoView();
}
break;
case Node.ELEMENT_NODE: // 1
queue.push(curr.childNodes[i]);
break;
}
}
}
});