I have a string that begins and ends with the table row tag <tr>...</tr>. There are multiple <td>s inside this, that take the form:
<td class="someClass">TextIWant TextI Do NOtWant</td><td><img src='green.png'></td>
<td class="someClass">TextIWant TextI Do NOtWant</td><td><img src='blue.png'></td>
What I'm looking to do, is look at the color.png, and add a class to the td, and trim some of the text inside the td. The final output should be something like:
<td class="someClass green">TextIWant</td>
<td class="someClass blue">TextIWant</td>
How can I do this with regular expressions?
You probably need to provide a lot more details about what you need to do, but the best course of action would be to use a DOM parser and JavaScript has a very nice one built in (IE8- is not supported).
// Select all odd `td` nodes and iterate over them
Array.prototype.forEach.call(document.querySelectorAll("td:nth-child(odd)"),
function (elem) {
// Get the immediately following sibling (with the img)
var imgTd = elem.nextSibling;
// Get the color from the <img>
elem.className += ' ' + imgTd.querySelector("img").getAttribute("src")
.replace('.png', '');
// Remove the sibling
imgTd.parentNode.removeChild(imgTd);
// Update the initial elements text to remove the undesired text
elem.textContent = elem.textContent.replace("TextI Do NOtWant", "");
});
http://jsfiddle.net/e9vrK/
Id do this with jquery, something like:
var td;
var img;
var color;
$('img').each(function(){
img=$(this);
td = img.closest('td').prev();
color = img.attr('src').split('.')[0]
td.html('the image is '+color+'.png').addClass(color);
});
demo here http://jsfiddle.net/QHkbe/2
Related
I have to pass HTML around in as a string (as I'm using postmessage for communication). To apply modifications to the html, I'm doing:
function foo(my_string) {
var temp, element_list;
temp = document.createElement("div")
temp.innerHTML = my_string;
element_list = temp.querySelectorAll(".foo");
...
My problem is that my_string can be anything and in case I'm passing a string with table rows and cells like this:
'<tr>' +
'<td>' +
'<a href="#gadget._key=project_module%2F1&gadget.view=view">' +
'My Test Project 2014/12/16 14:24:48.930904 GMT' +
'</a>' +
'</td>' +
'...' +
'</tr>'
appending this to a <div> removes the table rows and cells and I'm left with links only. Something like this:
'<a href="#gadget._key=project_module%2F1&gadget.view=view">' +
'My Test Project 2014/12/16 14:24:48.930904 GMT' +
'</a>' +
Question:
Is there a generic element, which accepts any type of child elements and does not modify whatever it's passed via innerHTML?
Thanks!
Edit:
The method is used to translate html snippets. When I'm updating a table, it will only pass the generated table rows vs receiving the whole table on the initial page rendering.
There isn't such an element. <tr> is a very good example of this. According to W3C standards, the "Permitted parent elements" for <tr> are "A <table>, <thead>, <tbody> or <tfoot> element."
If you must have these strings coming in as they are, your best bet is to perform some sort of detection as to the type of element(s) you are inserting, and wrap them in the appropriate HTML if required.
For example: (View as a CodePen)
HTML
<div id="container"></div>
JavaScript
var anyone = "<div>In a Div</div>";
var tableOnly = "<tr><td>In a..</td></tr>" +
"<tr><td>...table</td></tr>";
$(function () {
var $container = $("#container");
appendContent(anyone);
appendContent(tableOnly);
function appendContent(html) {
var $html = $(html),
$parent = $(),
lastParent = "";
$html.each(function () {
var parent = parentTag(this.tagName);
if(parent !== lastParent)
{
$container.append($parent);
$parent = $(parent);
}
$parent.append(this);
lastParent = parent;
});
$container.append($parent);
}
function parentTag(tagName) {
switch (tagName.toLowerCase()) {
case "tr":
return "<table></table>";
default:
return "<div></div>";
}
}
});
Edit: Note that the technique used here to detect the tags used in your HTML can have problems if your HTML contains content that cannot be part of the same parent. For example, the following code would fail:
appendContent("<tr><td>Also in a table</td></tr><div>Also in a div</div>");
This is because of how jQuery internally builds its selectors. Since you can't have a div tag as a sibling to a tr, effectively the div element gets dropped. Here's a CodePen demonstrating this, but from the sound of things, this wouldn't be an issue for the OP's needs. If it is, you could use some alternative method of detecting the tags such as Regular Expressions.
If you append the mal-formatted HTML data (as you've noticed) with missing tags you're at the Browser DOM parser mercy removing every single one till a conformable HTML is returned.
If your main concern (project-wise) is just about table HTML content than you could
treat the string as an XML data structure and get the needed wrapping tag and act accordingly:
jsBin demo
function sanitizeHTML( string ) {
// Treat friendly a HTMLString as XML structure:
var par = new DOMParser();
var doc = par.parseFromString(string, 'text/xml');
var chd = doc.firstChild;
var tag = chd.nodeName.toUpperCase(); // Get the tag
var ele;
function wrapWith(parent, childEl){ // Wrap a node into a parent
var p = document.createElement(parent);
p.appendChild(childEl);
return p; // And return that parent element.
}
if(/^(THEAD|TBODY|TR)$/.test(tag)){ // If THEAD or TBODY or TR
ele = wrapWith("table", chd); // just wrap in TABLE.
}else if(/^(TD|TH)$/.test(tag)){ // Else if TD or TH
ele = wrapWith("tr", chd); // wrap first in TR
ele = wrapWith("table", ele); // and than in TABLE.
}else{
// All fine. Do we need something here?
}
return ele || chd; // Returns a HTMLElement
}
// This will return the final HTMLElement:
// var myEl = sanitizeHTML( str );
// Let's see in console:
console.log( sanitizeHTML( str ).outerHTML );
For simplicity sake the above code will consider strings with only one children.
To extend it - loop all the children of the doc object.
See this jsfiddle: http://jsfiddle.net/Grezzo/x1qxjx5y/
With <tr>s in <table>s they are ok.
It's because you are putting a <tr> in a <div> which isn't valid.
Putting unsanitized content in the page like this is a real security risk
Update: I updated the jsfiddle to include two <div>s that were not modified by javascript and you can see that the <tr>s are stripped if they are not in a <table> parent: http://jsfiddle.net/Grezzo/x1qxjx5y/1/
I'm trying to make a kind of simple search engine, where
the user enters a string and if it's equal to the text inside
an element, that portion of text must be highlighted some way.
This is the html:
<input type="text">
<input type="button" value="Change text"><br>
Click here to get more info!
this is the css:
.smallcaps{
color:red;
}
and this is the jquery function that makes the search and replace:
$("input[type='button']").click(function(){
var textValue = $("input[type=text]").val();
$("a").html(function(_, html) {
return html.replace(new RegExp(textValue,"ig"), '<span class="smallcaps">'+textValue+'</span>');
});
});
This is an example of how it looks like:
Everything works fine, until the search string is equals to the name of a node element, so for example if the search string is a, the html will be broken.
How can I avoid the replace of the html itself?. I just want to work over the text.
This is the codepen:
http://codepen.io/anon/pen/mefkb
Thanks in advance!
I assume that you want to only highlight the last search and not store the ones from before.
With this assumption, you can store the old value if it is the first call and use the stored value in the calls afterwards:
$("input[type='button']").click(function(){
// Escape the html of the input to be able to search for < or >
var textValue = $('<div/>').text($("input[type=text]").val()).html();
if(textValue === '') return;
$("a").html(function(_, html) {
var old = $(this).data('content');
if(!old) {
old = html;
$(this).data('content', old);
}
var replacer = function(match) {
return match.replace(new RegExp(textValue, "ig"), '<span class="smallcaps">'+textValue+'</span>');
};
if(/[<>]/.test(old)) {
return old.replace(/^[^<>]*</gi, replacer).replace(/>[^<>]*</gi, replacer).replace(/>[^<>]*$/gi, replacer);
}
return replacer(old);
});
});
Also i fixed two bugs I found when testing:
if you search for an empty string, everything is broken.
If you search for html characters like < or > nothing is found as in the text they are converted to < or >.
One thing is not solved, as it is not possible to easily implement it without destroying the subelement structure: It is not possible to search in different subelements, as you have to remove the tags, search then and insert the tags at the right position afterwards.
Working fiddle: http://codepen.io/anon/pen/KlxEB
Updated Demo
A workaround would be to restore <a> to original text, instead of complicating the regex.
Your problem is a form the <span> tag is getting replaced.
var init = $("a").text(); //save initial value
$("input[type='button']").click(function(){
$('a').text(init); //replace with initial value
var textValue = $("input[type=text]").val();
$("a").html(function(_, html) {
return html.replace(new RegExp(textValue,"ig"), '<span class="smallcaps">'+textValue+'</span>');
});
});
I have a string where I want to remove all figure tags. I have tried the following:
var s = '<html><body>report content<figure id="fig2" data-contenttype="chart"><img src="chart.jpg"/><div>chart 1</div></figure><div>body content</div><figure id="fig2"><img src="chart2.jpg"/><div>chart 2</div></figure></body></html>';
var result = $(s).find('figure').remove();
The reason this does not work is that find does not find the figure elements because they have children. Does anyone know how I can remove all figure nodes (and everything inside them) and leave the rest of the html in tact?
Note the html is not in the DOM I need to do this via string manipulation. I don't want to touch the DOM.
You can wrap your string in a jQuery object and do some sort of a manipulation like this:
var removeElements = function(text, selector) {
var wrapped = $("<div>" + text + "</div>");
wrapped.find(selector).remove();
return wrapped.html();
}
USAGE
var removedString = removeElements('<html><body>report content<figure id="fig2" data-contenttype="chart"><img src="chart.jpg"/><div>chart 1</div></figure><div>body content</div><figure id="fig2"><img src="chart2.jpg"/><div>chart 2</div></figure></body></html>','figure');
The beauty of this approach is that you can specify a jquery selector which to remove.
Another approach for keeping html and body tag:
var s = '<html><body>report content<figure id="fig2" data-contenttype="chart"><img src="chart.jpg"/><div>chart 1</div></figure><div>body content</div><figure id="fig2"><img src="chart2.jpg"/><div>chart 2</div></figure></body></html>';
var $s = s.replace(/<figure>(.*)<\/figure>/g, "");
console.log($s)
How to select a tag having a specific text inside it and nothing else? For example if I have the following:
<table>
<tr>
<td>Assets</td><td>Asset</td>
</tr>
<tr>
<td>Play</td><td>Players</td><td>Plays</td>
</tr>
</table>
Is there any way that I may select the <td>Asset</td> and nothing else. I tried it with contains i.e. $("table tr td:contains(Play)") but it returns all the tds in the second tr (as it should). Whereas I just want <td>Play</td>.
Is there any way to achieve this, like there's a way to select elements based on their attributes. Is there any way to select elements based on the text inside them?
How about that:
var lookup = 'Asset';
$('td:contains('+ lookup +')').filter(function() {
return $(this).text() === lookup;
});
Demo
Try before buy
Try something like this :
$("table tr td").filter(function() {
return $(this).text() === "Play";
})
If it was an input field you can specify something similar, but a little more exact with $('input[name~="Plays"]) so that it would filter out every other word, leaving the value isolated.
Other than that, the only way I know of doing this with a table is with what you had, but also throwing a conditional statement to check the text inside them.
Here is my version of accomplishing this:
http://jsfiddle.net/combizs/LD75y/3/
var play = $('table tr td:contains(Play)');
for (var i = 0, l = play.length; i < l; i++) {
if ($(play[i]).text() === "Play") {
// your script to modify below
$(play[i]).css({"color" : "red"});
}
}
I am trying to find // (slashes) in the document and wrap it with a span.
I've tried
var slashes = "//";
/slashes+/
So output should be:
Hello There! I Am <span class="slashes">//</span> An Alien
With jQuery .replace() and :contains but nothing happens, and I am new to reguler expressions to do this correctly. How would I do this?
Edit: What have I tried:
Solution for this question didn't work:
function slashes($el) {
$el.contents().each(function () {
dlbSlash = "//";
if (this.nodeType == 3) { // Text only
$(this).replaceWith($(this).text()
.replace(/(dlbSlash)/gi, '<span class="slashes">$1</span>'));
} else { // Child element
slashes($(this));
}
});
}
slashes($("body"));
You need to escape the slashes in your regex. Try
var mystring = "adjfadfafdas//dsagdsg//dsafda"
mystring.replace(/\/\//g,'<span class="slashes">\/\/</span>');
Should output
"adjfadfafdas<span class="slashes">//</span>dsagdsg<span class="slashes">//</span>dsafda"
If you want to replace the slashes in h2 and p tags, you can loop through them like so:
$('h2, p').each(function(i, elem) {
$(elem).text(
$(elem).text().replace(/\/\//g,'<span class="slashes">\/\/</span>'));
});
This will blow away any additional html tags you may have had in your p and h2 tags, though.
This is one more way of doing this
//Find html inside element with id content
var html = $('#content').html();
//Replace // with <span style='color:red'>//</span>
html = html.replace(/\/{2}/g,"<span style='color:red'>$&</span>");
//Return updated html back to DOM
$('#content').html(html);
and here is the demo
I think you were looking in the right place. The only thing to fix is your regular expression:
.replace(/\/\//g, '<span class="slashes">$1</span>'));
Focusing on text nodes (type 3) is important, instead of doing a global replace of the body innerHTML that might break your page.
If you want to apply such replacement for single // only, go with
mystring = mystring.replace(/(\/{2})/g, "<span class=\"slashes\">$1</span>");
However if you want to apply that for 2 or more slashes, then use
mystring = mystring.replace(/(\/{2,})/g, "<span class=\"slashes\">$1</span>");
But if you want to apply it for any even quantity of slashes (e.g. //, ////, etc.) then you need to use
mystring = mystring.replace(/((?:\/{2})+)/g, "<span class=\"slashes\">$1</span>");
Test the code here.