I got the following HTML:
<div id="editable_phrase">
<span data-id="42">My</span>
<span data-id="43">very</span>
<span data-id="1">first</span>
<span data-id="21">phrase</span>
</div>
and I need to get the data-id attributes when I select (highlight) with a mouse these words. I use the following code:
var data = window.getSelection().getRangeAt(0).cloneContents();//this gets the data for all selected words
console.log(data);
It works fine except that when I select last word phrase, it selects only text without html contents. Any ideas how to fix that? I can use jQuery.
If I select 2 or 3 words, I need to get their data-ids respectively to each word, as it is with getRangeAt(0).cloneContents(). The problem is only with the last word, which does not return HTML code.
Thank you.
EDIT:
There has been a similar thread before, here is a working solution:
https://jsfiddle.net/hallleron/wg1pbwbf/2/
Basically you loop through the siblings in the selection to get each value and then parse the array as string to display it in my result paragraph for better visuals.
ORIGINAL:
If you want a jQuery-free version, here is a fiddle: https://jsfiddle.net/hallleron/wg1pbwbf/
The whole Javascript Part is the following:
document.getElementById('editable_phrase').addEventListener("click", getDataId);
function getDataId(){
console.log(window.getSelection().anchorNode.parentElement.attributes[0].nodeValue);
}
So every time the event listener detects a click, it gets the selected text/span and extracts its data-id attribute from the object.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="editable_phrase">
<span data-id="42">My</span>
<span data-id="43">very</span>
<span data-id="1">first</span>
<span data-id="21">phrase</span>
</div>
<script>
$('#editable_phrase').on('click','span',function(){
var res = $(this).attr('data-id');
alert(res);
})
</script>
I am using the example Individual column searching from DataTables.Net which works for the most part, however, I have two fields that it will not filter on.
I believe its down to there being a FontAwesome icon in the field too.
This is the HTML for one of the table columns that is not filtering, the other is virtualy the same so if i can get this one working, the other one should too.
<td class='ReportManager'>
<span style='opacity: 0.349019607843137; font-size: 20px;'>
<i class='fa fa-file-o'></i>
</span></br>
81
</td>
I suspect if I strip out the icon, this would work, but I need the icon in there, and for the filter to work.
Any and all help would be very much appreciated.
After scratching my head, and looking at this in a fiddle (where this annoyingly worked) I output the value that the filter was looking for, then compared against the html that was being written.
The JavaScript was looking for a <br> tag rather then a <br/> or <\br> tag.
I want to select and return searched text using jQuery.
The problem is; parts of the text may be located in <span> or other inline elements, so when searching for 'waffles are tasty' in this text: 'I'm not sure about <i>cabbages</i>, but <b>waffles</b> <span>are</span> <i>tasty</i>, indeed.', you wouldn't get any matches, while the text appears uninterrupted to people.
Let's use this HTML as an example:
<div id="parent">
<span style="font-size: 1.2em">
I
</span>
like turtles
<span>
quite a
</span>
lot, actually.
<span>
there's loads of
</span>
tortoises over there, OMG
<div id="child">
<span style="font-size: 1.2em">
I
</span>
like turtles
<span>
quite a
</span>
lot, actually.
TURTLES!
</div>
</div>
With this (or similar) JavaScript:
$('div#parent').selectText({query: ['i like', 'turtles', 'loads of tortoises'], caseinsensitive: true}).each(function () {
$(this).css('background-color', '#ffff00');
});
//The (hypothetical) SelectText function would return an array of wrapped elements to chain .each(); on them
You would want to produce this output: (without the comments, obviously)
<div id="parent">
<span style="font-size: 1.2em">
<span class="selected" style="background-color: #ffff00">
I
</span> <!--Wrap in 2 separate selection spans so the original hierarchy is disturbed less (as opposed to wrapping 'I' and 'like' in a single selection span)-->
</span>
<span class="selected" style="background-color: #ffff00">
like
</span>
<span class="selected" style="background-color: #ffff00"> <!--Simple match, because the search query is just the word 'turtles'-->
turtles
</span>
<span>
quite a
</span>
lot, actually.
<span>
there's
<span class="selected" style="background-color: #ffff00">
loads of
</span> <!--Selection span needs to be closed here because of HTML tag order-->
</span>
<span class="selected" style="background-color: #ffff00"> <!--Capture the rest of the found text with a second selection span-->
tortoises
</span>
over there, OMG
<div id="child"> <!--This element's children are not searched because it's not a span-->
<span style="font-size: 1.2em">
I
</span>
like turtles
<span>
quite a
</span>
lot, actually.
TURTLES!
</div>
</div>
The (hypothetical) SelectText function would wrap all selected text in <span class="selected"> tags, regardless of whether parts of the search are located in other inline elements like <span>, '', etc. It does not search the child <div>'s contents because that's not an inline element.
Is there a jQuery plugin that does something like this? (wrap search query in span tags and return them, oblivious to whether parts of the found text may be located in other inline elements?)
If not, how would one go about creating such a function? This function's kinda what I'm looking for, but it doesn't return the array of selected spans and breaks when parts of the found text are nested in other inline elements.
Any help would be greatly appreciated!
Piece of cake! See this.
Folded notation:
$.each(
$(...).findText(...),
function (){
...
}
);
In-line notation:
$(...).findText(...).each(function (){
...
}
);
Three options:
Use the browser's built-in methods for this. For the finding, IE has TextRange with its findText() method; other browsers (with the exception of Opera, last time I checked, which was a long time ago) have window.find(). However, window.find() may be killed off without being replaced at some point, which is not ideal. For the highlighting, you can use document.execCommand().
Use my Rangy library. There's a demo here: http://rangy.googlecode.com/svn/trunk/demos/textrange.html
Build your own code to search text content in the DOM and style it.
The first two options are covered in more detail on this answer:
https://stackoverflow.com/a/5887719/96100
Since I just so happened to be working on a similar thing right now, in case you'd like to see the beginnings of my interpretation of "option 3", I thought I'd share this, with the main feature being that all text nodes are traversed, without altering existing tags. Not tested across any unusual browsers yet, so no warranty whatsoever with this one!
function DOMComb2 (oParent) {
if (oParent.hasChildNodes()) {
for (var oNode = oParent.firstChild; oNode; oNode = oNode.nextSibling) {
if (oNode.nodeType==3 && oNode.nodeValue) { // Add regexp to search the text here
var highlight = document.createElement('span');
highlight.appendChild(oNode.cloneNode(true));
highlight.className = 'selected';
oParent.replaceChild(highlight, oNode);
// Or, the shorter, uglier jQuery hybrid one-liner
// oParent.replaceChild($('<span class="selected">' + oNode.nodeValue + '</span>')[0], oNode);
}
if (oNode.tagName != 'DIV') { // Add any other element you want to avoid
DOMComb2(oNode);
}
}
}
}
Then search through things selectively with jQuery perhaps:
$('aside').each(function(){
DOMComb2($(this)[0]);
});
Of course, if you have asides within your asides, strange things might happen.
(DOMComb function adapted from the Mozilla dev reference site
https://developer.mozilla.org/en-US/docs/Web/API/Node)
I wrote a draft as a fiddle. The main steps:
I made a plugin for jQuery
$.fn.selectText = function(params){
var phrases = params.query,
ignorance = params.ignorecase;
wrapper = $(this);
. . .
return(wrapper);
};
Now I can call the selection as a $(...).selectText({query:["tortoise"], ignorance: true, style: 'selection'});
I know you want to have iterator after the function call, but in your case it is impossible, because iterator have to return valid jQuery selectors. For example:
word <tag>word word</tag> word is not valid selector.
After sanitizing the content of wrapper, for each search makeRegexp() makes personal regular expression.
Each searched piece of html source goes to emulateSelection() and then wrapWords()
Main idea is to wrap in <span class="selection">...</span> each single piece of phrase not separated by tags, but not analyze the whole tree of nodes.
NOTE:
It's NOT working with <b><i>... tags in html. You have to make corrections in regexp string for it.
I not guarantee it will work with unicode. But who knows...
As I understood, we talking about iterators like $.each($(...).searchText("..."),function (str){...});.
Check the David Herbert Lawrence poem:
<div class="poem"><p class="part">I never saw a wild thing<br />
sorry for itself.<br />
A small bird will drop frozen dead from a bough<br />
without ever having felt sorry for itself.<br /></p></div>
Actually, after rendering, browser will understood it like this:
<div class="poem">
<p class="part">
<br>I never saw a wild thing</br>
<br>sorry for itself.</br>
<br>A small bird will drop frozen dead from a bough</br>
<br>without ever having felt sorry for itself.</br>
</p>
</div>
For example, I looking for the phrase: "wild thing sorry for". Therefore, I have to highligt the exerpt:
wild thing</br><br>sorry for
I can not wrap it like this <span>wild thing</br><br>sorry for</span>, then create jQuery selector by some temporary id="search-xxxxxx", and return it back -- it's wrong html. I can wrap each piece of text like this:
<span search="search-xxxxx">wild thing</span></br><br><span search="search-xxxxx">sorry for</span>
Then I have to call some function and return jQuery array of selectors:
return($("[search=search-xxxxx]"));
Now we have two "results": a) "wild thing"; b) "sorry for". Is it really what you want?
OR
You have to write you own each() function like another plugin to jQuery:
$.fn.eachSearch = function(arr, func){
...
};
where arr will be not an array of selectors, but array of arrays of selectors, like:
arr = [
{selector as whole search},
{[{selector as first part of search]}, {[selector as second part of search]}},
...
]
I have a main page which has ckeditor to perform some text editing.
ckeditor contains below elements:
<address>Address<address><pincode>123456</pincode>
I try to get focused element as
editorinstance1.focus();
var temp =editorinstance1.getSelection().getStartElement();
or
var temp =editorinstance1.document.getSelection().getStartElement();
when I click before the pincode element's first char
actually I need cursor position like
<pincode>[cursor here]123456</pincode>
the code working fine except chrome
chrome return address element.
how do I get correct element.
Just I checked with HTML it also same problem.(http://jsfiddle.net/z5ABt/2/)
I also reported in code.google.com/p/chromium/issues/detail?id=337757
This seems to be a bug in Chrome, when you click on the first character it focuses the wrong edit area. When you don't use nested contenteditable, it seems to work fine.
<panel>
<div id="address" contenteditable="true">Address</div>
<span contenteditable="true">, </span>
<div contenteditable="true" id="pin">123456</div>
</panel>
Clicking on the 1 will focus the correct edit area.
I can't find a ticket in their bug tracker about this specific issue, but they do have a number of issues regarding nested contenteditable. You might want to make a bug report.
this is a bug in chrome
you may use only like below.
<panel contenteditable="true">
<div id="address" contenteditable="true">Address</div>
<span contenteditable="true">, </span>
<span contenteditable="false"></span>
<div contenteditable="true" id="pin">123456</div>
</panel>
In a page I'm working on I have this HTML (simplified version; the original is a bit more complex).
<a href="alink.php" >
<b>1</b>
<span name="aName" data-editable="text" ></span>
<span class="type">numeric</span>
</a>
Then I have a system that allows an "edit mode".
That edit to mode changes that HTML to this:
<a href="alink.php" >
<b>1</b>
<span name="aName" data-editable="text" ><input name="aName" type="text"></span><img src="ok.png"><img src="x.png"></span>
<span class="type">numeric</span>
</a>
The issue is as follows:
When the user clicks the input how can I have the carret where the user clicked without anything else happening?
For that I tried this:
If I use the preventDefault(), the user is not sent to the link but the carret is also not positioned where the user clicked.
If I use stopPropagation(), nothing is prevented, the link is clicked.
If I use both, same as preventDefault() happens.
One possible solution I thought is to get rid of the <a> and replace it with a different tag, like a <span> tag. I just would prefer not to have to do that due to how this system works. If you think that there's a nice alternative, then please state it.
No examples or answers with libraries please
Edit: My relevant js code, as requested:
this.editableElement = document.createElement('input');
this.editableElement.type = "text";
this.editableElement.onclick = function (e){
e.preventDefault();
e.stopPropagation();
}.bind(this);
this.editableElement.name = this.parent.getAttribute('name');
this.editableElement.value = this.currentText;
Edit2: jsfiddle as requested.
http://jsfiddle.net/brunoais/gZU8C/
Now try to place the caret where you clicked. You'll check that the input becomes selected, the link is not followed but the caret is not placed where you clicked.