Finding the textNode which the cursor is currently over - javascript

Let's say I have the following...
<div id="text">Some text</div>
When my mouse goes over Some, Some will be returned and the same with text.
Is this possible without putting each node into it's own element?

That is pretty much impossible. Since in Javascript you just know that you are hovering over an element (div and/or a textnode in this case), you can't know which word is hovered just like that.
Maybe with alot of effort and some geeky hacks on offsets and/or event.pageX/Y, but I would go for the solution you mentioned yourself, wrapping each word into its own element.
var $text = $('#text');
$text.html($text.text().replace(/\b(\w+)\b/g, "<span class='word'>$1</span>"));
$('.word').bind('mouseenter', function(e){
alert($(this).text());
});

This isn't possible without either wrapping the two nodes in elements or somehow determining the exact position of the text nodes and then binding to some parent's click event. The former option is simpler. Just wrap them in spans:
<div id="text"><span>Some<span> <span>text</span></div>
Or, if you need to wrap them all automatically:
jQuery.fn.getTextNodes = function(){
return this.not('script').contents().map(function(){
var n = this.nodeType;
return n === 3 ? this : n === 1 ? jQuery(this).getTextNodes().get() : [];
});
};
jQuery('#text').getTextNodes().replaceWith(function(){
return jQuery(this.data.replace(/\S+/g, '<span>$&</span>')).filter('span').click(function(){
alert('You just clicked on the word: ' + jQuery.text([this]));
}).end();
});​ ​
Demo: http://jsfiddle.net/PYKqM/

Related

Using jQuery to find matching text in any child text node?

I'm a bit rusty on my jQuery (been a few years!) I have to assume this has been asked 100 times but I'm not searching for the right terms it seems.
I'm trying to grab all of the descendant text nodes of an object, and then, if there's a match to a text string, do something with that object. Here's why I have now:
$(".my-item").each(function(){
var content = $(this).find('*').text();
if (content.indexOf('mySearchString'>0)){
//do something
}
})
I can't seem to get this to find all text, however. Just the direct descendents. Sample HTML:
<div class="my-item">
this text is part of the var 'content'
<div>
this text is not
</div>
</div>
You have to access the textContent of each node, and you could do directly in the filter, and return the matches, or use a regular each loop if you want to work with them directly
$(".my-item").each(function(){
var $children = $(this).children();
$children.contents().each(function(i,node){
if (node.nodeType === 3 && node.textContent.indexOf('mySearchString') !== -1) {
//do something
}
});
});
FIDDLE

Trying to replace a word with the same word wrapped in a span element

I have a contenteditable div. I am trying to hunt for the word "design" and wrap it in a span element.
HTML:
<div id="text"></div>
JavaScript:
$("#text").prop("contenteditable", true);
var string = $("#text")[0].innerHTML;
$("#text").innerHTML = string.replace("design", "<span>design</span>");
This doesn't work. My first guess would be that its because the script runs once, so by the time I type "design" it doesn't catch it. So, I tried putting it in a setInterval, but that didn't work either. JSFiddle
Any help would be much appreciated.
$("#text").prop("contenteditable", true).keypress(function(e){
$(this).html(function(i,html){return html.replace(/<span>design</span>/g,"design").replace(/design/g, "<span>design</span>")});
});
I didn't test, but hopefully it'll work.
You can do it like this
$('#text').prop("contenteditable", true).on('keyup change',function(){
var $el = $(this);
$el.find('span').contents().unwrap(); // remove existing spans around "design"
$el.html(function(i,v){
return v.replace(/\bdesign\b/gi, "<span>design</span>") // wrap design with spans
});
setEndOfContenteditable(this); // reset the cursor to the end
});
FIDDLE
the setEndOfContenteditable function was taken from this SO Answer - it sets the cursor back to the end of the text
https://stackoverflow.com/a/3866442/1385672

Appending elements to DOM with indentation/spacing

Here is an example. Check the console for the result. The first two divs (not appended; above the <script> in the console) have the proper spacing and indention. However, the second two divs do not show the same formatting or white space as the original even though they are completely the same, but appended.
For example the input
var newElem = document.createElement('div');
document.body.appendChild(newElem);
var another = document.createElement('div');
newElem.appendChild(another);
console.log(document.body.innerHTML);
Gives the output
<div><div></div></div>
When I want it to look like
<div>
<div></div>
</div>
Is there any way to generate the proper white space between appended elements and retain that spacing when obtaining it using innerHTML (or a possible similar means)? I need to be able to visually display the hierarchy and structure of the page I'm working on.
I have tried appending it within an element that is in the actual HTML but it has the same behavior
I'd be okay with doing it using text nodes and line breaks as lincolnk suggested, but it needs to affect dynamic results, meaning I cannot use the same .createTextNode(' </br>') because different elements are in different levels of the hierarchy
No jQuery please
I think you're asking to be able to append elements to the DOM, such that the string returned from document.body.innerHTML will be formatted with indentation etc. as if you'd typed it into a text editor, right?
If so, something like this might work:
function indentedAppend(parent,child) {
var indent = "",
elem = parent;
while (elem && elem !== document.body) {
indent += " ";
elem = elem.parentNode;
}
if (parent.hasChildNodes() && parent.lastChild.nodeType === 3 && /^\s*[\r\n]\s*$/.test(parent.lastChild.textContent)) {
parent.insertBefore(document.createTextNode("\n" + indent), parent.lastChild);
parent.insertBefore(child, parent.lastChild);
} else {
parent.appendChild(document.createTextNode("\n" + indent));
parent.appendChild(child);
parent.appendChild(document.createTextNode("\n" + indent.slice(0,-2)));
}
}
demo: http://jsbin.com/ilAsAki/28/edit
I've not put too much thought into it, so you might need to play with it, but it's a starting point at least.
Also, i've assumed an indentation of 2 spaces as that's what you seemed to be using.
Oh, and you'll obviously need to be careful when using this with a <pre> tag or anywhere the CSS is set to maintain the whitespace of the HTML.
You can use document.createTextNode() to add a string directly.
var ft = document.createElement('div');
document.body.appendChild(ft);
document.body.appendChild(document.createTextNode(' '));
var another = document.createElement('div');
document.body.appendChild(another);
console.log(document.body.innerHTML);

How to target text between two elements

Update Below
I'm trying to target the output of codemirror and add some custom events and styling to module elements.
Codemirror displays the following code as such.
<div><module type="content"></module><span>can contain other data</span></div>
In the DOM it is rendered between a series of spans.
<pre>
<span class="cm-tag"><div><module</span>
<span class="cm-attribute">type</span>
=
<span class="cm-string">"content"</span>
<span class="cm-tag">></module><span></span>
can contain other data
<span class="cm-tag"></span></div></span>
</pre>
The issue I'm having is trying to add a yellow background to the whole module element, but because the "=" part is between two elements, I'm not sure how to target it with a selector.
This is what I have right now, but because it does not include the text between the elements, there are gaps in the background color.
$('.cm-tag:contains("<module")').each(function () {
var $closingElement;
$(this).nextAll().each(function () {
if ($(this).text() == "></module>") {
$closingElement = $(this).next();
return false;
}
});
var $module =$(this).add($(this).nextUntil($closingElement));
$module.addClass('module');
});
Anyone have suggestion/ideas about how to accomplish this?
Update
I was able to get part way there by using the wrapAll jquery method, but the visible result still isn't quite right. Now the spaces and equal characters are removed from the wrapped element and placed after it.
<modulename"content"id"1234"/> = =
function hilightModules() {
$('.cm-tag:contains("<module")').each(function() {
var $module = $(this);
$(this).nextAll().each(function() {
$module = $module.add($(this));
// closing element
if ($(this).hasClass('cm-tag')) {
return false;
}
});
$module.wrapAll('<span class="module" />').click(function() {
// Do stuff
});
});
};
For adding click handlers to content text, your best bet is to just register a mousedown handler on the CodeMirror wrapper element, and, in that handler, determine whether the thing clicked is what you are looking for. Content elements may change at any time, and you don't want to register tons of handlers.
As for highlighting things, I'd recommend an overlay mode (see http://codemirror.net/demo/mustache.html for an example), rather than trying to do it with DOM munging (for the reasons listed above).
Like #Raminson said, you could target the <pre> tag to make the background span the entire section. Is this what you are looking for?
http://jsfiddle.net/ckaufman/CSzny/

select created element

$(iframe).bind('keypress', function (e) {
if (e.which == 13) {
var range = iframe.getSelection().getRangeAt(0);
var nodeText = $(range.startContainer, iframe).parent().html();
var leftPart = nodeText.substr(0, range.endOffset);
var rightPart = nodeText.substr(range.endOffset);
$(range.startContainer, iframe).parent().replaceWith('<big>' + leftPart + '</big><p>' + rightPart + '</p>');
return false;
}
});
I've got iframe with some content, e.g:
<p>someText</p>
When i place cursor between "some" and "text", and press enter, i want it to be splitted into this:
<big>some</big><p>Text</p>
everything seems to be working ok, but I also need to change cursor position to the beginnings of this: <p>Text</p>
I know how to set cursor position, but I need to select that element. just $('p', iframe) won't work, because I can have multiply <p> items in iframe. any ideas?
This is an unholy mix of DOM, which considers everything in terms of nodes and offsets, and jQuery-ish HTML-as-strings. The two do not mix well. You've misunderstood the endOffset and startOffset properties of DOM Ranges: they're not offsets within the innerHTML of the container. I suggest looking at MDC or the spec and refactoring your code to only use DOM nodes and not strings of HTML.
Denis ,
Give a common class name to all of them , so that it will effect on the elements.
I would add a dynamic id attribute (or use the metadata plugin) using jQuery to identify the split paragraph. And based on the identified split string place the cursor before the <p> tag. Do make sure to remove the id attribute (or some metadata) once you are done placing the cursor, so that any other splits or a backspace on the same <p> doesn't result in unintended consequences
Hope that helps
I found a nice solution without adding id or class to element. i changed this:
$(range.startContainer, iframe).parent().replaceWith('<big>'+leftPart+'</big><p>'+rightPart+'</p>');
to this:
var leftObject = $('<big>'+leftPart+'</big>');
var rightObject = $('<p>'+rightPart+'</p>');
$(range.startContainer, iframe).parent().replaceWith(leftObject);
leftObject.after(rightObject);
now i got both elements selected in leftObject and rightObject

Categories

Resources