I have the following HTML structure:
<div class="content">
<p>somecontent</p>
<p>another content <span id="name-1">content</span> 1234214</p>
</div>
I want to wrap only numbers in additional span (1234214). So far I've made this:
jQuery(window).load(function() {
jQuery('.content p').html(function(index, value) {
return value.replace(/(\d+)/g, '<span class="mathjaxfont">$1</span>');
});
});
However this replaces the 1 in span id. How can I exclude checking element attributes?
You might want not only to exclude attributes (think about the h1-element for example) but constrain your replacing on the text nodes. See this questions for some ideas on how to get only and work with text nodes: How do I select text nodes with jQuery?
This answer in above question How do I select text nodes with jQuery? gives you a collection of text-nodes on which you can do your string-replacing.
You should use .contents() and .replaceWith() for this:
jQuery('.content p').contents().each(function() {
var method = this.nodeType == 1 ? 'html' : 'replaceWith';
$(this)[method](this.textContent.replace(
/(\d+)/g,
'<span class="mathjaxfont">$1</span>'
));
});
Here's a JSFiddle.
Long and hard solution, but should work in nested elements.
The idea is to handle element's .html() string character by character, wrapping numbers when they are found, but omitting numbers inside tags' definition.
Fiddle.
$(document).ready(function()
{
$('.content p').each(function()
{
$(this).html(handleHtml($(this).html()));
});
});
function handleHtml(html)
{
var resultHtml = "";
var numberStr = "";
var re = /[0-9]/;
var isTag = false, quote = "";
for (var i = 0; i < html.length; i++)
{
var char = html.substr(i, 1);
if (!isTag && re.test(char))
{
numberStr += char;
}
else
{
if (numberStr)
{
resultHtml += wrapNumber(numberStr);
numberStr = "";
}
resultHtml += char;
if (isTag && !quote && (char == '"' || char == "'"))
{
quote = char;
}
else if (quote && quote == char)
{
quote = "";
}
if (char == '<')
{
isTag = true;
}
else if (!quote && char == '>')
{
isTag = false;
}
}
}
if (numberStr)
{
resultHtml += wrapNumber(numberStr);
}
return resultHtml;
}
function wrapNumber(number)
{
return '<span class="mathjaxfont">' + number+ "</span>";
}
Related
I want to replace all matched text with another text, but I don't want replace if that text is in the alt or href attribute.
Example:
<p>Hello world!</p>
<p><img src="hello.jpg" alt="Hello"/></p>
Hello
My code:
var replacepattern = new RegExp('Hello', 'gi');
newcontent = newcontent.replace(replacepattern, function(match, contents, offset, s) {
var link = 'demo.com'
index++;
if (link != '') {
return '' + match + '';
} else {
return match;
}
});
It works perfect with text only. How can I match text except img src, alt etc?
You can use jQuery itself to help you with the replacement:
$(html)
.contents()
.filter(function() {
return this.nodeType == 1 || this.nodeType == 3;
}).each(function() {
this.textContent = this.textContent.replace(replacepattern, 'whatever');
});
Note that the last occurrence of Hello is not replaced, because it's technically invalid to have a text node as a child of <body>.
Also, you would have to modify it to work in IE < 9 or 10; basically the browser is expected to support node.textContent :)
Update
The problem was slightly more complicated; or maybe my mind is making it more difficult than it is. Replacing text nodes with jQuery ain't the easiest to do, so some pure JS is required for that:
$('<div><p>Hello world!</p><p><img src="hello.jpg" alt="Hello"/></p>Hello</div>')
.find('*')
.andSelf()
.each(function() {
for (var i = 0, nodes = this.childNodes, n = nodes.length; i < n; ++i) {
if (nodes[i].nodeType == 3) {
var txt = nodes[i].textContent || nodes[i].innerText,
newtxt = txt.replace(/Hello/g, 'Bye');
if (txt != newtxt) {
var txtnode = document.createTextNode(newtxt);
this.replaceChild(txtnode, nodes[i]);
}
}
}
})
.end()
.end()
.appendTo('body');
How do i use javascript/jquery to select all $('td.created') and split the html on <br>, then wrap each section in span tags (so that i can add a class to span:first in order to style it).
The format of the string which is returned from $('td.created').html() is something like
posted by User123 <br> Posted on 1/2/12 at 4:15PM
Possible universal solution (works not only for 2 lines):
$("td.created").each(function() {
var text = this.innerHTML.split("<br>");
for (var i = 0; i < text.length; i++) {
var span = $("<span />").html(text[i]);
if (i == 0) span.addClass("first");
span.appendTo("#element");
}
});
DEMO: http://jsfiddle.net/7xQAL/
$('td.created').each(function(i, html) {
var newHtml = $(this).contents(),
spans = [];
newHtml.each(function() {
var html = (this.nodeName.toLowerCase() == 'br') ? '<br>' : '<span>' + this.textContent + '</span>';
spans.push(html);
});
$(this).html(spans.join(''));
});
DEMO ( using DIV Container)
http://jsfiddle.net/PVLek/
The following codes doesn't work and the result is broken because there are white spaces in a HTML tag.
HTML:
<div>Lorem ipsum <a id="demo" href="demo" rel="demo">dolor sit amet</a>, consectetur adipiscing elit.</div>
Javascript:
var div = document.getElementsByTagName('div')[0];
div.innerHTML = div.innerHTML.replace(/\s/g, '<span class="space"> </span>');
How to replace replace white spaces which are not in HTML tags?
It would be a better idea to actually use the DOM functions rather than some unreliable string manipulation using a regexp. splitText is a function of text nodes that allows you to split text nodes. It comes in handy here as it allows you to split at spaces and insert a <span> element between them. Here is a demo: http://jsfiddle.net/m5Qe8/2/.
var div = document.querySelector("div");
// generates a space span element
function space() {
var elem = document.createElement("span");
elem.className = "space";
elem.textContent = " ";
return elem;
}
// this function iterates over all nodes, replacing spaces
// with space span elements
function replace(elem) {
for(var i = 0; i < elem.childNodes.length; i++) {
var node = elem.childNodes[i];
if(node.nodeType === 1) {
// it's an element node, so call recursively
// (e.g. the <a> element)
replace(node);
} else {
var current = node;
var pos;
while(~(pos = current.nodeValue.indexOf(" "))) {
var next = current.splitText(pos + 1);
current.nodeValue = current.nodeValue.slice(0, -1);
current.parentNode.insertBefore(space(), next);
current = next;
i += 2; // childNodes is a live array-like object
// so it's necessary to advance the loop
// cursor as well
}
}
}
}
You can deal with the text content of the container, and ignore the markup.
var div = document.getElementsByTagName('div')[0];
if(div.textContent){
div.textContent=div.textContent.replace(/(\s+)/g,'<span class="space"> </span>';
}
else if(div.innerText){
div.innerText=div.innerText.replace(/(\s+)/g,'<span class="space"> </span>';
}
First split the string at every occurrence of > or <. Then fit together all parts to a string again by replacing spaces only at the even parts:
var div = document.getElementsByTagName('div')[0];
var parts = div.innerHTML.split(/[<>]/g);
var newHtml = '';
for (var i = 0; i < parts.length; i++) {
newHtml += (i % 2 == 0 ? parts[i].replace(/\s/g, '<span class="space"> </span>') : '<' + parts[i] + '>');
}
div.innerHTML = newHtml;
Also see this example.
=== UPDATE ===
Ok, the result of th IE split can be different then the result of split of all other browsers. With following workaround it should work:
var div = document.getElementsByTagName('div')[0];
var sHtml = ' ' + div.innerHTML;
var sHtml = sHtml.replace(/\>\</g, '> <');
var parts = sHtml.split(/[<>]/g);
var newHtml = '';
for (var i = 0; i < parts.length; i++) {
if (i == 0) {
parts[i] = parts[i].substr(1);
}
newHtml += (
i % 2 == 0 ?
parts[i].replace(/\s/g, '<span class="space"> </span>') :
'<' + parts[i] + '>'
);
}
div.innerHTML = newHtml;
Also see this updated example.
=== UPDATE ===
Ok, I have completly changed my script. It's tested with IE8 and current firefox.
function parseNodes(oElement) {
for (var i = oElement.childNodes.length - 1; i >= 0; i--) {
var oCurrent = oElement.childNodes[i];
if (oCurrent.nodeType != 3) {
parseNodes(oElement.childNodes[i]);
} else {
var sText = (typeof oCurrent.nodeValue != 'undefined' ? oCurrent.nodeValue : oCurrent.textContent);
var aParts = sText.split(/\s+/g);
for (var j = 0; j < aParts.length; j++) {
var oNew = document.createTextNode(aParts[j]);
oElement.insertBefore(oNew, oCurrent);
if (j < aParts.length - 1) {
var oSpan = document.createElement('span');
oSpan.className = 'space';
oElement.insertBefore(oSpan, oCurrent);
var oNew = document.createTextNode(' ');
oSpan.appendChild(oNew);
}
}
oElement.removeChild(oCurrent);
}
}
}
var div = document.getElementsByTagName('div')[0];
parseNodes(div);
Also see the new example.
$(document).ready(function() {
$("[class^='count[']").each(function() {
var elClass = $(this).attr('class');
var minWords = 0;
var maxWords = 0;
var countControl = elClass.substring((elClass.indexOf('['))+1, elClass.lastIndexOf(']')).split(',');
if(countControl.length > 1) {
minWords = countControl[0];
maxWords = countControl[1];
}
else { maxWords = countControl[0]; }
$(this).after('<div class="wordCount"><strong>0</strong> words so far</div>');
if(minWords > 0) {
$(this).siblings('.wordCount').addClass('error');
}
$(this).bind('keyup click blur focus change paste', function() {
var numWords = jQuery.trim($(this).val()).split(' ').length;
if($(this).val() === '') {
numWords = 0;
}
$(this).siblings('.wordCount').children('strong').text(numWords);
if(numWords < minWords || (numWords > maxWords && maxWords != 0)) {
$(this).siblings('.wordCount').addClass('error');
}
else {
$(this).siblings('.wordCount').removeClass('error');
}
});
});
});
this script basically counts the spaces between words but if extra spaces are added it counts them as new words too...
http://blog.themeforest.net/tutorials/creating-a-jquery-word-counter/
.split() also accepts regular expressions. Try this:
var numWords = jQuery.trim($(this).val()).split(/\s+/).length;
Remove all double spaces with a Regex and then do your script. If there are no double spaces to find, it cannot count them.
This is an example in C#, but the same accounts for javascript: How do I replace multiple spaces with a single space in C#?
Is it possible to generate the most specific XPath expression automatically from the position of the cursor on the web page?
The XPath expression would change with "onMouseMove event".
If it's possible, how would you implement it? Or is it already implemented in some Javascript or Python library? I would prefer it in Python with a combination of some web library but Javascript would be good and acceptable too.
See the Get XPath thread in DZone Snippets for finding the XPath. See the How do I check if the mouse is over an element in jQuery? question here for identifying when the mouse cursor is over an element.
I have answered an almost identical question (using jQuery) at Return XPath location with jQuery? Need some feedback on a function
If you change the click event to mouseenter you would have what you ask for..
$(document).delegate('*','mouseenter',function(){
var path = $(this).parents().andSelf();
var xpath='/';
for (var i = 0; i < path.length; i++)
{
var nd = path[i].nodeName.toLowerCase();
xpath += '/';
if (nd != 'html' && nd != 'body')
{
xpath += nd;
if (path[i].id != '')
{
xpath += '#' + path[i].id;
}
else
{
xpath += '['+ ($(path[i-1]).children().index(path[i])+1) +']';
}
if (path[i].className != '')
xpath += '.' + path[i].className;
}
else
{xpath += nd;}
}
$('#xpath').html(xpath); // show the xpath in an element with id xpath..
return false;
});
Demo at http://jsfiddle.net/gaby/hsv97/25/
Update with no jQuery used.. (for modern browsers)
function getXpath(event){
var hierarchy = [],
current = event.srcElement||event.originalTarget;
while (current.parentNode){
hierarchy.unshift(current);
current = current.parentNode;
}
var xPath = hierarchy.map(function(el,i){
return el.nodeName.toLowerCase() + ((el.id !== '') ? '#'+el.id : '') + ((el.className !== '') ? '.'+el.className.split(' ').join('.') : '');
}).join('/');
document.getElementById('xpath').innerHTML = xPath;
return xPath;
}
if (document.addEventListener){
document.addEventListener('mouseover', getXpath, false);
} else {
document.onmouseover = getXpath;
}
Demo at http://jsfiddle.net/gaby/hsv97/29/
vanilla javascript (with indices) http://jsfiddle.net/nycu2/1/
function nodeindex(element, array) {
var i,
found = -1,
element_name = element.nodeName.toLowerCase(),
matched
;
for (i = 0; i != array.length; ++i) {
matched = array[i];
if (matched.nodeName.toLowerCase() === element_name) {
++found;
if (matched === element) {
return found;
}
}
}
return -1;
}
function xpath(element, suffix) {
var parent, child_index, node_name;
parent = element.parentElement;
if (parent) {
node_name = element.nodeName.toLowerCase();
child_index = nodeindex(element, parent.children) + 1;
return xpath(parent, '/' + node_name + '[' + child_index + ']' + suffix);
} else {
return '//html[1]' + suffix;
}
}
function xpathstring(event) {
var
e = event.srcElement || event.originalTarget,
path = xpath(e, '');;
document.querySelector('.xpathresult').value = path;
highlight();
}