Does anyone know if it is possible with javascript to to tell the position of a mouse click in some text? I know it's easy to get the x,y coordinates, but I'm wanting the position in the text.
For example if I clicked inside <p>foo bar</p>
I want to be able to tell that the click was on/after the 5th character. Or that foo b is before the click and ar is after it.
By the way, I'm using jQuery too, I'm happy with pure JS and solutions that use jQ.
Thanks in advance.
Javascript and browsers don't support this directly, but you could technically wrap each character in a span and do it based on which span was clicked on:
<p>
<span>f</span>
<span>o</span>
<span>o</span>
<span> </span>
<span>b</span>
<span>a</span>
<span>r</span>
</p>
If anybody actually tries this, be prepared to be eaten by a velociraptor :p
Expanding on codeka's answer and my comment . . . try this (i'm assuming your target p has id my_p):
(function() {
var p = $('#my_p');
var o_string = p.text();
p.html('<span>' + o_string.split('').join('</span><span>') + '</span>');
$('span', p).each(function(i) {
$(this).data('MyIndex', i);
}).click(function() {
var char_index = $(this).data('MyIndex');
if (char_index >= 5) {
alert('You clicked after character 5! Before: "' + o_string.substring(0, char_index) + '", After (inclusive): "' + o_string.substring(char_index) + '"');
}
});
})();
What that does is:
1. Find the paragraph where you need per-character clicking knowledge, 2. Split the text in that paragraph into a number of one-character spans, 3. Inform each of those one-character spans of its position in the string, and 4. Assign a click() handler to each span, which spits out the information about the span (including your example of char index >= 5, and printing the before and after parts of the string).
Of course you might want to put that in $(document).ready(...) instead of an anonymous function; I didn't know if maybe you had a precondition for activating this detection though.
Frankly I don't like very much idea of travesting text and 'span'ing it. But I'm not quite sure what you are looking for, if you need just the word clicked and do dblclick instead of click will do, i have next code:
$('*').dblclick(function(event){
console.log((window.getSelection() || document.getSelection() || document.selection.createRange().text).toString());
event.stopPropagation();
});
after that you can deselect text if you want.
Without toString() you will get object that has a lot of properties, study it as well.
Related
I'm working on writing a bookmarklet to enhance my workflow at work. Part of my job is obtaining the correct information to be placed into an email. I love JavaScript and jQuery, so I'm working on a way to make my job easier using this library.
I'm targeting a website that has particularly odd markup. I need to capture the text after a matched label tag, and before the next label tag. This is all, oddly enough, inside a P tag. I have no clue why the website's developer decided to use label tags either... I am unable to modify the markup, so that's not an option. I've searched all across the web and haven't been able to find a working technique for my specific situation.
I created a jsFiddle to demonstrate what I'm trying to do using the same kind of markup and CSS. I have no problem accessing the label, and I've used a few different methods to do so (in the fiddle, commented out) but I am still unable to properly "capture" the text in between the two label tags. The text will end up being placed into an alert so I can quickly copy it. I've tried using .nextUntil but have had no luck with it.
Basically it would be something like this:
<label>item 1</label> Content to capture
<br><br>
<label>item 2</label> Don't capture this...
I fear the reason my attempts aren't working is because (I think) nextUntil() tries to find the next object using the initial selector, so it's looking for the next label, rather than the text in between. I've tried using $('selector').parent().nextUntil('label') which also hasn't worked.
Here's the working example:
$(document).ready(function(){
//$('p label:eq(0)')afterUntil('<br>').css('color', 'red');
//$('p').find($('label:contains("item 1")')).nextUntil("<label>").css('color', 'red');
$('p label:contains("item 1")').parent().nextUntil('label').css('color','red');
});
label {
display:inline-block;
width:25%;
font-weight:bold;
}
p {
font-family:arial;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>
<label>item 1</label> Capture me!<br><br>
<label>item 2</label> Don't capture me
</p>
(fiddle: http://jsfiddle.net/1c2LpzrL/1/)
You can just treat the HTML inside of the <p> tag as a string and then get the substring between the </label> and the first <br>:
var totalText = $("p").html();
//determine start-pos and end-pos of desired substring, and then get it
var startPos = totalText.indexOf("</label>") + "</label>".length;
var endPos = totalText.indexOf("<br");
var targetText = totalText.substring(startPos,endPos).trim();
(fiddle: http://jsfiddle.net/3uw8ux9t/3/)
startPos finds the position of the first "</label>" and then adds the length of "</label>" to that.
endPos finds the position of the first "<br" (I left the closing > out because officially it's spelled <br />, my way allows for both ways of spelling).
targetText finally takes the substring from the startPos to the endPos.
(.trim() removes any empty spaces from the start and end of your new string)
console.log(targetText) gives:
Capture me!
UPDATE:
After your comment, I rewrote my script to accommodate for your specified needs:
$(document).ready(function(){
function getUnenclosedText(selector,pointer,tag) {
var str = $(selector).html();
//determine start-pos and end-pos
var startPos = str.indexOf(pointer+"</"+tag+">") + (pointer+"</"+tag+">").length;
var endPos = str.indexOf("<"+tag,startPos);
//if there are line-breaks, reset end-pos
if (str.indexOf("<br",startPos)<endPos || endPos==-1) {
endPos = str.indexOf("<br",startPos);
}
//return substring
if (endPos==-1) {return str.substring(startPos).trim();} //if it was the last text in the container
else {return str.substring(startPos,endPos).trim();}
}
console.log(getUnenclosedText("p","item 1","label")); //(selector,pointer,pointerTag)
alert('Item 1: '+getUnenclosedText("p","item 1","label") +'\n'+ 'Item 3: '+getUnenclosedText("p","item 3","label"));
});
p {
font-family:arial;
}
label {
display:inline-block;
width:25%;
font-weight:bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>
<label>item 1</label> Capture me!
<br /><br />
<label>item 2</label> Don't capture me
<label>item 3</label> capture me as well
<br /><br />
<label>item 4</label> Don't capture me either
</p>
(fiddle: http://jsfiddle.net/3uw8ux9t/9/)
I tried to make it as scalable as possible, using variables for all the parameters, so that the script is not limited to <p> and <label> anymore.
You now have to call the function getUnenclosedText(selector,pointer,tag), every time you want to extract a piece of text. The three parameters make the function scalable, so you can use it on various elements, not just <label> in <p>:
"selector" specifies which container element(s) you want to perform the function on. So if you have multiple <p>tags with different ID's for example, you can access a specific <p> tag, using its jQuery selector (e.g. "p#someid").
"pointer" specifies after which piece of text (e.g. "item 1", "item 2") you want to extract the unenclosed text.
"tag" specifies the tag-type that encloses the pointer (e.g. "label", "span").
If you have any questions, just ask them in the comments and I'll answer them or update this answer if need be, but I think you can find most of what you need on the internet.
Read this about how to use indexOf(), and you'll understand the most difficult parts of the code.
$('p label:contains("item 1")').prop('nextSibling') will select that text node after the label.
If you want to style that using css then you'll have to use jQuery to wrap that text in a span and set the css color to red. Either that or color the content on the <p> tag red and set the color of that label back to it's original color.
Also keep in mind that what you get back from .nextSibling will be a text node and not a jQuery object.
If the problem is to find text/HTML between labels, you could split the raw HTML text by the label tags into Array.
var items = paragraphNode.innerHTML.split(/<label>[\s\S]*?<\/label>/g);
The nice thing with this solution is that the regular expression can easily be changed to support other tags or more complex structures.
Demo is here:
http://jsfiddle.net/x2u8ysx2/
You can't get that specific text using only jQuery, because it only deals with elements. The text that you want is between elements, and the parent element contains more text than you want.
You can get the DOM nodes from that label and until the next label, and get the text content from them.
In your example there are two text nodes and two br elements between the labels, so you would need to decide what you want from the br elements. In the example I have translated them to line breaks in the text:
var e = $('p label:eq(0)')[0].nextSibling;
var s = '';
while (e.tagName != 'LABEL') {
if (e.tagName == 'BR') {
s += '\n';
} else {
s += e.nodeValue;
}
e = e.nextSibling;
}
console.log(s);
Demo: http://jsfiddle.net/Guffa/1c2LpzrL/3/
jsfiddle DEMO
function captureStr(p, itm) {
if(p.find('label').length > 0 && p.find('label:eq(0)').text().indexOf(itm) >= 0)
return p.html().split("<br>")[0].split("</label>")[1].trim();
}
To test:
console.log(captureStr($('p'), "item 1"));
Capture me!
If you have many of these structure then you can loop through and call the function for each one.
Assuming the structure doesn't change much from what you posed the following will write the nodes out to the console based on the next sibling not being empty. I think this should work as many labels as you have in any number of paragraph tags. Here is a working JSFiddle (http://jsfiddle.net/RVAProgrammer/jsqxfgxe/)
console.log($('p').contents().filter(function () {
var isTextNode = this.nodeType === Node.TEXT_NODE
if (isTextNode) {
if ($(this)[0].nextElementSibling === null) {
return false;
}
return true;
}
return false;
}).text());
I have a lot of text within a DIV. I would like to be able to search it on a string using perhaps a PHP regex then find the y position of that text that is searched on so I can place an image next to it.
I believe this would be the best way of doing it, I am unsure. However, I can't figure out how to determine how many pixels from the top of the screen that line is. Any recommendations?
#user1524441 not sure if this what you mean but from what I understand you want to add an image next to a specific search term, if this is the case you can use the following approach assuming you are using jquery:
var search = "Robin";
var text = $('.content').text();
$('.content').html(text.replace(search,'<span class="search-term" style="font-weight:bold;">' + search + '</span>'));
$('.search-term').before('<img src="url/goes/here" alt="" title=""/>');
here is a jsfiddle displaying how it works http://jsfiddle.net/6YsfB/
you can use substring and indexOf
var serch = "Robin"
var text = "hi my name is Robin, what you so talk? I'm god";
document.write(text.substring(text.indexOf(serch),text.indexOf(serch)+(serch).length));
http://jsfiddle.net/5bejQ/1/
If you're doing the search on the server, you could output the found text wrapped in <span> tags with a certain CSS class:
Here is some text with the <span class = "highlighted">text of interest</span> highlighted.
Then you could use the CSS "content" property (http://css-tricks.com/css-content/) in conjunction with the :before or :after pseudo-classes to show an image before or after the highlighted text.
I Just wanted to know, is there any way though which we can display only First Word from h2 Heading.
For Example:
In the source code it should look like this
<h2> Stackoverflow is an Ideal Place</h2>
However, on live website it should look like this
Stackoverflow
Basically, we want to display the FIRST WORD from the whole heading. However, Search engine should read the complete title.
Try this way
$(document).ready(function(){
var ac = $("#ac").text().split(' ');
$("#ac").text(ac[0])
})
You cannot do this with just CSS. You have pseudo-element selector for first letter, first line, but not first word.
You can set a width so that only the first word is visible (and use overflow:hidden of course), but that's not foolproof for all font families and sizes.
Finally you can do it with plain JavaScript or jQuery.
Plain JS:
var el = document.getElementById("ac");
el.innerHTML = el.innerHTML.split(/\s/)[0];
jQuery:
$("#ac").html(function(i, h) { return h.split(/\s/)[0];});
This will do what you want it to...
<h2 id="ac">Stackoverflow is an Ideal Place</h2>
$('#ac').html( function(i, h) {
var words = h.split(/\s/);
return ' <h2>' + words[0] + '</h2>';
});
Say, I have:
<p> This is a sentence </p>
And when a user clicks on words in the sentence (this, is, a, sentence), the web page will know that specific word is clicked. Is there something in js or the jQuery library for that?
I want to do that for my project in Django so that when they click on a word, I know which word in the sentence is clicked (not the whole sentence itself).
I only have 2 ideas for that:
Split the sentence into different elements so that it separates words when its clicked using javascript and getElementByID
OR
Similar to #1 but I just use the anchor tag and pass the link and the value of the link in the same form so that I know which word in the sentence is passed () I'm guessing there's something wrong with this too.
Is there any better way? Or what is the best way?
We did this once, it was a really nasty thing to do on a whole document, but for a paragraph it wasn't bad.
Take the text node, split it on white space. Then, wrap each word in a span and put it back into the document.
Attach a listener to the PARENT NODE and use delegation to figure out which span was clicked on.
I think using multiple elements is the most standad compliant way, however...
...using a monospace font like courier, where you can preditc the width of each letter due to the font's monospaced nature, you should be able to track the x position of the mouse in the sentence.
You can use pageX to get the mouse X coordinates. Its client.x in IE.
var properMethod = event.clientX || event.pageX;//This should select only the available non null property for the browsers
I would go like that:
html:
<span>hello</span><span>world</span>
jquery:
$("span").click(function() {
alert($(this).text());
});
If you didn't want to wrap every word in a tag beforehand, you could try doing it after the click. If you had separate paragraphs, you would only need to convert the one that the user clicked within.
For example (http://jsfiddle.net/scovetta/TU9rs/1/)
$('#paragraph').live('click', function($evt) {
var original_text = $(this).text();
var word_list = $(this).text().split(' ');
var word_html = '';
// Convert each word to an individual span
$.each(word_list, function(_, word) {
word_html += '<span class="word">' + word + '</span> ';
});
// Replace original paragraph with the spans
$(this).text('').html(word_html);
// Search through each span for one in the right spot
$('#paragraph .word').each(function(_, elt) {
var rect = elt.getBoundingClientRect();
if ($evt.pageX >= rect.left &&
$evt.pageY >= rect.top &&
$evt.pageX <= rect.right &&
$evt.pageY <= rect.bottom) {
alert('You clicked on the word: ' + $(elt).text());
return;
}
});
// Clean up after ourselves
$(this).html('').text(original_text);
});
This attaches click events to all words in a specified selector. If that selector has any other child elements it skips it.
jsFiddle:
http://jsfiddle.net/iambriansreed/bAExr/
jQuery
$('p').each(function(){
var $this = $(this);
if($this.children().length) return;
$this.html(function(i, old) {
return old.replace(/\b(\w+?)\b/g, '<span>$1</span>')
});
$this.children().click(function(e) {
alert(e.target.innerHTML);
});
});
jsFiddle Demo
You need to replace the html using javascripts .replace(regex, changedStuff) to create clickable events for each of the words.
$('p').html(function(i, old) {
return old.replace(/\b(\w+?)\b/g, '<span class="clickable">$1</span>')
}).click(function(e) {
alert(e.target.innerHTML);
});
Right now the html shows up if you click between words, but you get the idea.
Is there an easy way to wrap spans around arbitrary text within an html paragraph? For example, given the following original html:
<p>Here is a dandy block of text to color up</p>
<p> WHOAH another paragraph</p>
I'd like to wrap arbitrary portions of the text based on user input. So one set of input might transform this into
<p>Here is a <span style="background:yellow">dandy block</span> of text to color up</p>
<p> WHOAH <span style="background:green">another paragraph</span></p>
While another set of input might create
<p>Here is a<span style="background:yellow">a dandy block</span> of text to color up</p>
<p> WHOAH <span style="background:green">another</span> paragraph</p>
This problem is related to this one and this one, however, the main difference with my goal is that I want the highlights to be permanent, not just temporary selections and I'd also like this to work within p elements rather than textareas.
If it's possible, I imagine it would look something like using jQuery
var innerText = $('p')[p_index].slice(char_start, char_end).text();
$('p')[p_index].slice(char_start, char_end).html(
"<span style=\"background:yellow\">"+
innerText +
"</span>");
This would (in theory) select the p_index paragraph, grab the range between the given indices and replace it with a newly created span which has the original text nested inside of it. This clearly doesn't work since subscripting on the jQuery object does not return another inner jQuery object. Though
$("p").slice(0, 1).html("<span style=\"background: blue\">" +
$("p").slice(0, 1).text() +
"</span>");
Does exactly what I want on a paragraph level, but not on the within text level. I could use this approach to do the replacement by totally writing each paragraph given the character ranges I have, but if there's an easy way, I'd greatly appreciate suggestions.
$("p")[p_index]
gives you the actual DOM element that is that paragraph at p_index, so to get the contents of the paragraph you'd need to use:
$("p")[p_index].innerHTML
// OR
$("p")[p_index].textContent
Using jQuery would be easier though. You wouldn't use the jQuery slice() method to reduce the range to a single element, you'd use the .eq() method. Try something like this:
$('p').eq(p_index).html(function(i,currentText) {
return currentText.substring(0, char_start) +
"<span style=\"background:yellow\">" +
currentText.substring(char_start, char_end) +
"</span>" +
currentText.substring(char_end);
});
When you pass a function to the .html() method, jQuery sets the html to whatever you return from the function. jQuery passes the function the current (inner) html of the element so you can process it. (If you do this on a jQuery object containing more than one element your function is called once for each element so they can be processed individually.)
Demo: http://jsfiddle.net/62HHk/
I've used this plugin in the past with nice results.
Try this:
$('input[type=text]').keyup(function() {
var val = $.trim(this.value);
var text = $('p').text().split(' ')
$.each(text, function(i, v) {
if (v == val) {
text[i] = '<span>'+v+'</span>';
}
})
$('p').html(text.join(' '))
})
Fiddle
This should work. It can easily be turned into a function that takes the word you're looking for as a parameter.
jQuery.textReplace by Ben Alman
$('.text').replaceText( /hello/g, '<span classs="interesting">hello</span>' );