Context
I have a website where users can write articles. After creating the article, other users should be able to go in and highlight any text within the article and make a comment on it (similar to the system on Medium). I do this by saving the user's highlight to a database and then checking the highlight against the article when it loads. However, the author of the article can bold and italicize text, which ruins the system because the <strong> and <i> tags get in the way. For example, if a user's article consisted of the following content:
<p>This is my article. <strong>Bolded text here.</strong></p>
and another user wanted to come in and highlight my article. Bolded text, that text would be saved to the database. Then, I insert the highlight into the article (which is just applying a span to the highlighted text) by using this code to replace the article's HTML with the highlight:
let $text = $("#articleContents");
let textCurrent = $text.html().trim();
let textToHighlight = text.trim();
let ifTextExists = textCurrent.indexOf(textToHighlight) > -1;
if (ifTextExists) {
textCurrent = textCurrent.replace(textToHighlight, "<span class='highlights'>" + textToHighlight + "</span>");
$text.html(textCurrent);
}
So, because I'm checking the article's HTML to see if a highlight matches, when the user wants to highlight the string my article. Bolded text, the content it is checked against is article. <strong>Bolded text</strong>, so the text is not highlighted (the class is not applied) because the tags get in the way. With Medium's system, any string can be highlighted regardless of whether it is bolded or not.
Question
How can I alter my code to disregard the non-text nodes and highlight the whole string, whether it's wrapped in tags or not?
Things I Have Tried
I have tried using .text() instead of .html(); the problem with that is that the HTML tags are removed from the article, which I need to be able to keep the structure of the article (for example, removing the div tags means
moving the text to a new line by pressing enter doesn't work).
Use $text.text() instead of $text.html() so it ignores tags.
You don't need JQuery for that.
JavaScript solution: element.innerText
const elem = document.querySelector('.elementSelector')
elem.innerText;
JQuery solution: $(elem).text();
$('.elementSelector').text();
Related
I'm looking for a way to look for a specific string within a page in the visible text and then wrap that string in <em> tags. I have tried used HTML Agility Pack and had some success with a Regex.Replace but if the string is included within a url it also gets replaced which I do not want, if it's within an image name, it gets replaced and this obviously breaks the link or image url.
An example attempt:
var markup = Encoding.UTF8.GetString(buffer);
var replaced = Regex.Replace(markup, "product-xs", " <em>product</em>-xs", RegexOptions.IgnoreCase);
var output = Encoding.UTF8.GetBytes(replaced);
_stream.Write(output, 0, output.Length);
This does not work as it would replace a <a href="product/product-xs"> with <a href="product/<em>product</em>-xs"> - which I don't want.
The string is coming from a text string value within a CMS so the user can't wrap the words there and ideally, I want to catch all instances of the word that are already published.
Ideally I would want to exclude <title> tags, <img> tags and <a> tags, everything else should get the wrapped tag.
Before I used the HTML Agility Pack, a fellow front end dev tried it with JavaScript but that had an unexpected impact on dropdown menus.
If you need any more info, just ask.
You can use HTML Agility Pack to select only the text nodes (i.e. the text that exists between any two tags) with a bit of XPath and modify them like this.
Looking only in body will exclude <title>, <meta> etc. The not excludes script tags, you can exclude others in the same way (or check the parent node in the loop).
foreach (HtmlNode node in htmlDoc.DocumentNode.SelectNodes("//body//*[not(self::script)]/text()"))
{
var newNode = htmlDoc.CreateTextNode(node.InnerText.Replace("product-xs", "<em>product</em>-xs"));
node.ParentNode.ReplaceChild(newNode, node);
}
I've used a simple replace, regex will work fine too, prob best to check the performance of each approach and choose which works best for your use case.
I know virtually nothing about Javascript. By a monkey-see, monkey-do approach I’ve managed to successfully use Javascript within AppleScript/Safari to fill text fields on a web-site using the following command:
do JavaScript "document.getElementById('ElementID').value ='TextToEnter';" in document 1
I’ve been able to enter text into all fields except one. The fields that work are labeled as input type="text”. The field that doesn’t work is complex in that the entered text can be formatted (bold, italics, underline, alignment, etc.) after entry. Assuming I’ve identified the correct source code for this element it looks as follows PRIOR TO any text entry:
<body id="tinymce" class="mce-content-body " onload="window.parent.tinymce.get('fax_text').fire('load');" contenteditable="true" spellcheck="false"><p><br data-mce-bogus="1"></p></body>
Depending on how its viewed, sometimes the p and br tags appear on separate lines but everything is otherwise identical.
After manual entry of text (“INSERT TEXT HERE”) directly into the web page's text field the source code becomes:
<body id="tinymce" class="mce-content-body " onload="window.parent.tinymce.get('fax_text').fire('load');" contenteditable="true" spellcheck="false"><p>INSERT TEXT HERE</p></body>
The following did not work (wrapped in Applescript):
document.getElementById('tinymce').value ='INSERT TEXT HERE';
It produces the error: "missing value".
As per #WhiteHat, the following with n= 0-4 inserted text at several spots on the page but not in the targeted text field; n > 4 resulted in the "missing value" error:
document.getElementsByTagName('p')[n].innerHTML ='Insert text here';
I tried targeting the br tag but to no avail. How do I target this text field with Javascript? Note: I do not need to format the entered text.
You need to access the <p> element, which is just after the body of the document, as such...
document.getElementsByTagName('P')[0].innerHTML = 'your text'
The getElementsByTagName function returns an array of all elements with the tag name you provide, P in this case. You're looking for the first one, hence the [0].
The innerHTML property will allow you to set the contents of the <p> element.
Following is a good JavaScript reference...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference
The following reference is for the web page, or Document Object Model (DOM).
https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model
And tinymce is a 3rd party JavaScript library which allows the rich edit functionality.
http://www.tinymce.com/
Based on the comments, the specific field you are looking for is named fax_text. Here is the source, it's in a textarea tag, take note on which function to use TagName vs. Name...
document.getElementsByName('fax_text')[0].value = 'This is my text!';
document.getElementsByTagName('textarea')[0].value =
document.getElementsByName('fax_text')[0].value +
'\nThis is additional text...';
<textarea rows="5" name="fax_text" cols="36" class="mytext"></textarea>
This text field is in an iFrame.
This iFrame contains an HTML document (<html><head><body>).
To get this document, you need the_iFrame.contentDocument.
do JavaScript "var ifr = document.getElementById('fax_text_ifr'); ifr.contentDocument.getElementsByTagName('p')[0].innerHTML = 'some text';" in document 1
What I am trying to do is to create a front-end editable tagbox (editable div). Whenever a user types a word into that box and presses , this box will change that word into a colorful label. The problem I am having is:
User types the first word in, presses the comma key.
The word is then wrapped in <a> tags.
User types the second word in, presses the comma key.
Now I have to leave the first wrapped word as it is and take only the second word into consideration to wrap it into an <a> tag as well. It's extremely tricky to me, I have no idea how to leave the first <a> tag alone and select "free" words for wrapping. This also means wrapping more than one word into a single <a> tag whenever the user decides to put a two-word tag. It has to work with any number of tags.
Could you please point me in the right direction? I am trying to solve this with jQuery. I don't necessarily need the code itself, because I know how to write it, I just need to come up with the right algorithm in my brain.
Alright, as requested :)
Depending on whether you keep the commas in the field after replacing or not, split the inner HTML of the editable content by comma and/or .
Try following
function wrapInLink(container){
var link_text = $(container).text().split(',').slice(-1).pop(); // finding the string for replacing with anghor tag
var html = $(container).html(); // getting the container html
html = html.replace(link_text, "<a href='link_to_be_given'>" + link_text + "</a>"); // replacing the link text with anchor tag
$(container).html(html); // replacing the container's html
}
This is a followup question to Removing <span> tag while leaving content intact, with just javascript
If I use spans to highlight text in a page, it breaks up the content into new nodes. And then, when I remove the highlight spans using replaceChild, the nodes remain separated. I would like to have the original text merged back into a single text node, instead of three text nodes - the text before the highlighting started, the text that was previously highlighted, and the text after the highlighting ended. Is this possible to do?
You could try something like
containerElement.innerHTML = containerElement.textContent;
Not sure that will work on IE prior to 9 though because of textContent.
Similar to Jim's suggestion but accommodates IE:
containerElement.innerHTML = containerElement.textContent || containerElement.innerText;
Or a much longer version:
var text = containerElement.textContent || containerElement.innerText;
while (containerElement.firstChild) {
containerElement.removeChild(containerElement.firstChild);
}
containerElement.appendChild(document.createTextNode(text));
I think the first is simpler.
I am looking to create a javascript/jquery function to wrap a piece of highlighted text from a textarea in strong tags - similar to the WYSIWYG editor here.
Is this possible and if so can you point me in the right direction.
EDIT:
OK so here's a hopefully clearer description of what I want...
I have a textbox on my page which I can type in.
I then want to be able to highlight a part of this text and wrap the highlighted part in <strong> tags
So if the text box had the words one two three and I highlighted the word "two", I want to be able to wrap that word in the strong tags - so becoming one <strong>two</strong> three
Hope this is clearer... I know there are plugins out there but I don't need the full WYSIWYG functionality.
My Rangy inputs (terrible name, I know) jQuery plug-in does this.
Example code:
$("#foo").surroundSelectedText("<strong>", "</strong>");
jsFiddle: http://jsfiddle.net/aGJDa/
I love Rangy! Use it often! But I didn't want to include the whole thing just for this little application, so I did it using document.execCommand to wrap the selected text, then used the href (third parameter of the CreateLink execCommand) to find the element, wrap it with what I wanted, and then remove the link:
document.execCommand('CreateLink', false, 'uniqueid');
var sel = $('a[href="uniqueid"]');
sel.wrap('<strong />')
sel.contents().unwrap();
document.execCommand is supported by all major browsers so you should be safe hacking it this way. In the browsers I've tested, the browser itself will close and open tags for you, so if you're selecting from the middle of one html tag to the middle of another, it should nest the tags correctly.