Removing comments and text nodes from jQuery collection - javascript

$html = $("<!-- comment --> <p>text</p>");
creates a jQuery collection like so
$( [the comment], [text node], p )
How can I access the paragraph only? .find("p") returns an empty collection
And, for extra points,
$html = $("<p>text</p>");
creates a jQuery collection like so
$( p )
Is there a fail safe way to get at the p, and only the p, that works whether the comment is there or not?

The simplest way is with filter and the universal selector *, which matches all elements.
$html = $("<!-- comment --> <p>text</p>").filter('*');

var p = $html.filter(function() { return this.nodeType === 1; });
jsFiddle.

Try this demo. Maybe this is not exactly how you're gonna use it, but you get the point.
<div class="text">
<!-- comment -->
<p>text</p>
</div>
var html = $('.text').html();
html = $.trim(html.replace(/<!--(.*?)-->/ig, ''));
alert(html);

One way is to get by index like in $html = $("<!-- comment --> <p>text</p>"); you can get p tag using $($html[2]).
OR
$html = $("<!-- comment --> <p>text</p>");
$target = new Object();
for(key in $html){
if(typeof $html[key] === 'object' && $html[key].localName === 'p')
$target = $html[key];
}

Related

How to select and change text using jquery [duplicate]

I have a HTML structure like this:
<div class="votes">
<b>5</b> Votes
<a id="vote-' + element_id +'" href="#" class="vote-btn"></a>
</div>
I have manage to get the text after 5 i.e. votes using:
var voteTextNode = $(this).parent('div').contents().filter(function() {
return this.nodeType == 3;
});
var voteText = voteTextNode.text();
now i want to change this text to vote which is respective number of votes . I have tried this:
voteNewText = ( newCount == '1' ) ? 'Vote' : 'Votes';
voteTextNode.text(voteNewText);
but it does not work for me. I have also tried the code from this link:
How can I get, manipulate and replace a text node using jQuery?
but it also wont work for me tell me where i am doing wrong
As you have seen, jQuery is not really made for handling text nodes. Your var voteTextNode will be a jQuery instance, holding a set of text nodes. You can hardly manipulate them using .text(), which would add some new TextNodes into them.
Yet, this should work:
$(this).parent('div').contents().each(function() {
// iterate over all child nodes
if (this.nodeType == 3)
this.data = "Vote";
});
But with plain dom methods it may be clearer:
var countTextNode, voteTextNode;
$(this).parent('div').find('b').each(function() {
countTextNode = this.firstChild;
voteTextNode = this.nextSibling;
});
return function setVotes(num) {
countTextNode.data = num;
votTextNode.data = num == 1 ? 'Vote' : 'Votes';
};
put it in a span
<div class="votes">
<b>5</b> <span id="votesTxt">Votes</span>
<a id="vote-' + element_id +'" href="#" class="vote-btn"></a>
</div>
and then
$("#votesTxt").text(( newCount == '1' ) ? 'Vote' : 'Votes');
EDIT if you don't wish to use span then just change the text for the element after the b tag:
$("#votes b:first")[0].nextSibling.data = (( newCount == '1' ) ? 'Vote' : 'Votes');
Treat it as the DOM tree it is: what you probably want is to get the first <b> element inside each <div> with class "votes" and then change the text in the text node that immediately follows it. jQuery is good at selecting and iterating over elements so use it for this part if you want. Once you've got the <b> elements, switch to regular DOM. Here's an example:
Demo: http://jsfiddle.net/Qq3T7/
Code:
$("div.votes").find("b:first").each(function() {
this.nextSibling.data = ($(this).text() == "1") ? " Vote" : " Votes";
});
Can you change the initial markup? You'll have a much easier time doing this if you just wrap the text you want to change in a tag:
<span id="votetext">Vote</span>
And then you can easily set the text:
$('#votetext').text('Votes');

How to strip specific tag into div in Javascript?

I have this html code
<div class="myDiv">
My link
<p>This is a paragraph</p>
<script>//This is a script</script>
</div>
And I this javascript:
$('.myDiv').children().each(
function() {
var strToStrip = $('.myDiv').html();
if ( this.tagName != 'A' ) {
// Strip tag element if tagName is not 'A'
// and replace < or > with < or >
strToStrip.replace(/(<([^>]+)>)(?!(a))/ig, "");
}
}
);
How can I strip all tags, except from the a element?
I only need the link and strip tags if it is not a link tag.
I can't find what wrong with this code and what regex can I use to do this.
Any help please?
Try this regex example:
var strToStrip = $('.myDiv').html();
var temp = strToStrip.replace(/<[^a\/][a-z]*>/g, "<");
var result = temp.replace(/<\/[^a][a-z]*>/g, ">");
alert(result);
My goal of this question is to figure out how twitter do his hashtag or usergroup by using # or #. Go here to see the final result
you can use replace method of string using regular expr
var html = $("#main").html();
var result = html.replace(/[\<\>\/]/g,'');
alert(result);
the example shown here

return the text value of tags and append a line break to each tag

So if I call this function:
$("#item").text()
on this HTML code:
<div id="item">
<pre><span class="cm-tag"><html></span></pre><pre><span class="cm-tab"> </span>asdf</pre><pre><span class="cm-tag"></html></span></pre>
</div>
it returns:
'<html> asdf</html>'
and i want it to return:
'<html>
asdf
</html>'
basically i need a new line after each <pre> tag... how would i do this?
A possible solution, get the text of each pre and join them with new lines:
var text = $("#item pre").map(function(){
return $(this).text();
}).get().join('\n');
Here is the jsfiddle: http://jsfiddle.net/uGGFe/
Another option for you:
var clone = $("#item").clone(); //create a clone we can manipulate
clone.find('pre').after('\n'); //stick new lines in after <pre> tags
alert(clone.text()); //behold the alert glory
http://jsfiddle.net/SrV9c/1/
var text = '';
$("#item pre").map(function(i, el) {
return $(el).text().replace(/\s/g, '')
}).each(function(i, val) {
if (i == 0)
text += val.concat('\n\t');
else
text += val.concat('\n');
});
​
Working sample
because jQuery search match htmlElement use regular expression, when regular expression match content first delete "\r\n", so you see the content not have "\r\n"

Replacing DIV content based on variable sent from another HTML file

I'm trying to get this JavaScript working:
I have an HTML email which links to this page which contains a variable in the link (index.html?content=email1). The JavaScript should replace the DIV content depending on what the variable for 'content' is.
<!-- ORIGINAL DIV -->
<div id="Email">
</div>
<!-- DIV replacement function -->
<script type="text/javascript">
function ReplaceContentInContainer(id,content) {
var container = document.getElementById(id);
container.innerHTML = content;
}
</script>
<!-- Email 1 Content -->
<script ="text/javascript">
var content = '<div class="test">Email 1 content</div>';
ReplaceContentInContainer('Email1',content);
}
</script>
<!-- Email 2 Content -->
<script ="text/javascript">
var content = '<div class="test">Email 2 content</div>';
ReplaceContentInContainer('Email2',content);
}
</script>
Any ideas what I've done wrong that is causing it not to work?
Rather than inserting the element as text into innerHTML create a DOM element, and append it manually like so:
var obj = document.createElement("div");
obj.innerText = "Email 2 content";
obj.className = "test"
document.getElementById("email").appendChild(obj);
See this working here: http://jsfiddle.net/BE8Xa/1/
EDIT
Interesting reading to help you decide if you want to use innerHTML or appendChild:
"innerHTML += ..." vs "appendChild(txtNode)"
The ReplaceContentInContainer calls specify ID's which are not present, the only ID is Email and also, how are the two scripts called, if they are in the same apge like in the example the second (with a corrected ID) would always overwrite the first and also you declare the content variable twice which is not permitted, multiple script blocks in a page share the same global namespace so any global variables has to be named uniquely.
David's on the money as to why your DOM script isn't working: there's only an 'Email' id out there, but you're referencing 'Email1' and 'Email2'.
As for grabbing the content parameter from the query string:
var content = (location.search.split(/&*content=/)[1] || '').split(/&/)[0];
I noticed you are putting a closing "}" after you call "ReplaceContentInContainer". I don't know if that is your complete problem but it would definitely cause the javascript not to parse correctly. Remove the closing "}".
With the closing "}", you are closing a block of code you never opened.
First of all, parse the query string data to find the desired content to show. To achieve this, add this function to your page:
<script type="text/javascript">
function ParseQueryString() {
var result = new Array();
var strQS = window.location.href;
var index = strQS.indexOf("?");
if (index > 0) {
var temp = strQS.split("?");
var arrData = temp[1].split("&");
for (var i = 0; i < arrData.length; i++) {
temp = arrData[i].split("=");
var key = temp[0];
var value = temp.length > 0 ? temp[1] : "";
result[key] = value;
}
}
return result;
}
</script>
Second step, have all possible DIV elements in the page, initially hidden using display: none; CSS, like this:
<div id="Email1" style="display: none;">Email 1 Content</div>
<div id="Email2" style="display: none;">Email 2 Content</div>
...
Third and final step, in the page load (after all DIV elements are loaded including the placeholder) read the query string, and if content is given, put the contents of the desired DIV into the "main" div.. here is the required code:
window.onload = function WindowLoad() {
var QS = ParseQueryString();
var contentId = QS["content"];
if (contentId) {
var source = document.getElementById(contentId);
if (source) {
var target = document.getElementById("Email");
target.innerHTML = source.innerHTML;
}
}
}
How about this? Hacky but works...
<!-- ORIGINAL DIV -->
<div id="Email"></div>
<script type="text/javascript">
function ReplaceContentInContainer(id,content) {
var container = document.getElementById(id);
var txt = document.createTextNode(content);
container.appendChild(txt);
}
window.onload = function() {
var args = document.location.search.substr(1, document.location.search.length).split('&');
var key_value = args[0].split('=');
ReplaceContentInContainer('Email', key_value[1]);
}
</script>

Finding out what line number an element in the dom occurs on in Javascript?

Though I've never heard of this but, is it possible to retrieve a node from the DOM using JS, and then find out on what line of the file that node occurred on?
I'm open to anything, alternative browsers plugins/add-ons etc...it doesn't need to be cross-browser per say.
I would assume that this would be possible somehow considering that some JS debuggers are capable of finding the line number within a script tag, but I'm not entirely sure.
Ok, forgive me for how large this is. I thought this was a very interesting question but while playing with it, I quickly realized that innerHTML and its ilk are quite unreliable wrt maintaining whitespace, comments, etc. With that in mind, I fell back to actually pulling down a full copy of the source so that I could be absolutely sure I got the full source. I then used jquery and a few (relatively small) regexes to find the location of each node. It seems to work well although I'm sure I've missed some edge cases. And, yeah, yeah, regexes and two problems, blah blah blah.
Edit: As an exercise in building jquery plugins, I've modified my code to function reasonably well as a standalone plugin with an example similar to the html found below (which I will leave here for posterity). I've tried to make the code slightly more robust (such as now handling tags inside quoted strings, such as onclick), but the biggest remaining bug is that it can't account for any modifications to the page, such as appending elements. I would need probably need to use an iframe instead of an ajax call to handle that case.
<html>
<head id="node0">
<!-- first comment -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<style id="node1">
/* div { border: 1px solid black; } */
pre { border: 1px solid black; }
</style>
<!-- second comment -->
<script>
$(function() {
// fetch and display source
var source;
$.ajax({
url: location.href,
type: 'get',
dataType: 'text',
success: function(data) {
source = data;
var lines = data.split(/\r?\n/);
var html = $.map(lines, function(line, i) {
return ['<span id="line_number_', i, '"><strong>', i, ':</strong> ', line.replace(/</g, '<').replace(/>/g, '>'), '</span>'].join('');
}).join('\n');
// now sanitize the raw html so you don't get false hits in code or comments
var inside = false;
var tag = '';
var closing = {
xmp: '<\\/\\s*xmp\\s*>',
script: '<\\/\\s*script\\s*>',
'!--': '-->'
};
var clean_source = $.map(lines, function(line) {
if (inside && line.match(closing[tag])) {
var re = new RegExp('.*(' + closing[tag] + ')', 'i');
line = line.replace(re, "$1");
inside = false;
} else if (inside) {
line = '';
}
if (line.match(/<(script|!--)/)) {
tag = RegExp.$1;
line = line.replace(/<(script|xmp|!--)[^>]*.*(<(\/(script|xmp)|--)?>)/i, "<$1>$2");
var re = new RegExp(closing[tag], 'i');
inside = ! (re).test(line);
}
return line;
});
// nodes we're looking for
var nodes = $.map([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(num) { return $('#node' + num) });
// now find each desired node in both the DOM and the source
var line_numbers = $.map(nodes, function(node) {
var tag = node.attr('tagName');
var tags = $(tag);
var index = tags.index(node) + 1;
var count = 0;
for (var i = 0; i < clean_source.length; i++) {
var re = new RegExp('<' + tag, 'gi');
var matches = clean_source[i].match(re);
if (matches && matches.length) {
count += matches.length;
if (count >= index) {
console.debug(node, tag, index, count, i);
return i;
}
}
}
return count;
});
// saved till end to avoid affecting source html
$('#source_pretty').html(html);
$('#source_raw').text(source);
$('#source_clean').text(clean_source.join('\n'));
$.each(line_numbers, function() { $('#line_number_' + this).css('background-color', 'orange'); });
},
});
var false_matches = [
"<div>",
"<div>",
"</div>",
"</div>"
].join('');
});
</script>
</head>
<!-- third comment -->
<body id="node2">
<div>
<pre id="source_pretty">
</pre>
<pre id="source_raw">
</pre>
<pre id="source_clean">
</pre>
</div>
<div id="node3">
<xmp>
<code>
// <xmp> is deprecated, you should put it in <code> instead
</code>
</xmp>
</div>
<!-- fourth comment -->
<div><div><div><div><div><div><span><div id="node4"><span><span><b><em>
<i><strong><pre></pre></strong></i><div><div id="node5"><div></div></div></div></em>
</b></span><span><span id="node6"></span></span></span></div></span></div></div></div></div></div></div>
<div>
<div>
<div id="node7">
<div>
<div>
<div id="node8">
<span>
<!-- fifth comment -->
<div>
<span>
<span>
<b>
<em id="node9">
<i>
<strong>
<pre>
</pre>
</strong>
</i>
<div>
<div>
<div>
</div>
</div>
</div>
</em>
</b>
</span>
<span>
<span id="node10">
</span>
</span>
</span>
</div>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Something like this?
var wholeDocument = document.getElementsByTagName('html')[0]
var findNode = document.getElementById('whatever')
var documentUpToFindNode = wholeDocument.substr(0, wholeDocument.indexOf(findNode.outerHTML))
var nlsUpToFindNode = documentUpToFindNode.match(/\n/g).length
This can be done. Start by getting the highest node in the document like this:
var htmlNode = document.getElementsByTagName('html')[0];
var node = htmlNode;
while (node.previousSibling !== null) {
node = node.previousSibling;
}
var firstNode = node;
(this code was tested and retrieved both the doctype node as well as comments above the html node)
Then you loop through all nodes (both siblings and children). In IE, you'll only see the elements and comments (not text nodes), so it'll be best to use FF or chrome or something (you said it wouldn't have to be cross browser).
When you get to each text node, parse it to look for carriage returns.
You could try: -
- start at the 'whatever' node,
- traverse to each previous node back to the doc begining while concatenating the html of each node,
- then count the new lines in your collected HTML.
Post the code once you nut it out coz thats a good question :)

Categories