In an html page you might have some code with two paragraphs like this:
<hr />
<p>The first paragraph</p>
<p>The second paragraph</p>
<hr />
and very simply, these two tags would render like this:
The first paragraph
The second paragraph
What I am interested in is to allow the user to click somewhere in the rendered html code so as to insert a new element with JQuery. For example if I click between the letter i and the letter r (just a click, no higlighting/selection)in the word f*ir*st found in the first paragraph, I would be able to insert a custom span element or whatever I like exactly in that position in the HTML code resulting in something like this:
<hr />
<p>The fi<span id="myCustomSpan"></span>rst paragraph</p>
<p>The second paragraph</p>
<hr />
Any ideas that can help me? My request excludes absolute positioning. That would not solve my issues.
This is dirty but makes use of contenteditable: http://jsfiddle.net/Jj9Mp/.
$('div').click(function(e) {
$('div').attr('contenteditable', true);
document.execCommand("InsertHTML", false, "<span class=red>!</span>")
e.stopPropagation();
});
$(':not(div)').click(function(e) {
if($(this).parents('div').length === 0) {
$('div').attr('contenteditable', false);
} else {
$('div').click();
}
e.stopPropagation();
});
Here is my solution : http://jsfiddle.net/gion_13/L6aeT/
It works by calculating the actual size (in px) of the string.
This can be done with somewhat complex scripting.
i can state an algorithm that you can try.
1.JQuery have an API offset() http://api.jquery.com/offset/ using that you can get the offset and element.
2.Now you need to take the innerHTML of the element, take it as string, from the offset Y val, split at position Y in the string. make the element as two elements.
3.Now you can directly create an Element in script using creteElement(tagname) and set the innerHTML and then insert it in between the two elements
Related
FIDDLE UPDATED TO CLASSES
<p id="boohoo"><b id="why did all this disappear">Old Text</b></p>
<p id="boohoo"><h4 id="why did this not work">Old Text</h4></p>
var x = $('#boohoo').text().replace('Old', 'New');
function whyNotBold() {
$('#boohoo').text(x);
}
Why is there a difference between <b> and <h4>? And how can I have the the former <b id="... html not disappear when I insert the text? I would think .text().replace(... would only replace text and not affect the html, but that doesnt seem to be the case here since it's deleting it.
Firt of all you have 2 element with the same id, this is wrong. So your replacement work with the first element only.
The second is text() function strips tags and return only text content of your first node which is: Old Text. When you use text() func to set the text, jquery thinks you want your node <p id="boohoo"> contains text ONLY and it makes this:
<p id="boohoo">New Text</p>
Becase text() func resonsible for text nodes
EDIT:
In your case, you can do what you want like this:
var x = $('b').text().replace('Old', 'New');
function whyNotBold() {
$('b').text(x);
$('h4').text(x);
}
setTimeout(whyNotBold, 300);
Demo
I have the following HTML code:
<p> hello every body, to day is a sunny day</p>
I have use wrap() method to wrap element with <em> tag like the following:
<p> hello every body, to day is <em>a sunny day<em></p>
When I finished my test, I use $("em").contents().unwrap() to unwrap all <em> tag:
I make a loop for all elements in my page
So I found that
-----hello every body, to day is a----
and
-----a sunny day-----
are 2 seperated text nodes,
How can I use wrap() and unwrap(), so my text won't be splited like that?
I'm afraid, you cannot use .wrap() and .unwrap() to do what I think you intend to do. .unwrap() calls replaceWith() in jQuery code which eventually calls .replaceChild(). The replaceChild() method in JS, replaces one DOM child node, with another. The use of .contents() is creating a text node.
This updates the render tree in the browser, which in turn updates the markup you see in a developer tool. The nodes in a render tree are placed on their own lines for the markup... not to be confused with what actually ends up being displayed: since \n is a whitespace character, the text nodes will show in one line on the screen.
If you need the actual text (innerHTML) to not be broken up as textNodes you will have to resort to somewhat drastic, measures:
$(document).ready(function () {
var _childContent = $('em').contents()[0].data,
_childIndex = $('p')[0].innerHTML.indexOf('<em>'),
_parentText;
$('em').remove();
_parentText = $('p')[0].innerHTML;
$('p')[0].innerHTML = _parentText.substr(0, _childIndex) +
_childContent +
_parentText.substr(_childIndex + 1, _parentText.length);
});
Due to having to take such measures, I would agree with grmmph's comment about if it is an issue of styling, then take the addClass() approach.
However, if what you want is to have all the text between the <p> and </p> tags appear on one line instead of being treated as textNodes, then the above works (at least in IE 10 when viewed in the developer tool).
This question goes back a while, so my answer may only be of use to people in the future facing similar problems to the OP (as I was myself).
A neat way to re-combine the unwrapped text nodes is by resetting the innerHtml of the span's parent element using $.html()
HTML
<p>Lorem ipsum <span>dolor sit</span> amet.</p>
JS
function reset () {
var parent = $('span').parent();
$('span').contents().unwrap();
parent.html(function(i, html) {
return html;
});
}
This has been asked so long ago, but because an answer hasn't been marked yet, I'll post an answer. I'm not sure if you wanted those text nodes split or not, but both ways can be done like this (run snippets to see it work):
The vanilla Javascript way (found here):
function unwrapElement () {
var el = document.querySelector('em')
var parent = el.parentNode
while (el.firstChild) parent.insertBefore(el.firstChild, el)
parent.removeChild(el)
}
<p>Hello every body, to day is <em>a sunny day<em></p>
<button onclick="unwrapElement()">Do unwrap</button>
The JQuery way (similar to #StevieP's way):
function unwrapElement() {
$('em').contents().unwrap()
$('em').parent().html((i, html) => html) // if you remove this, then the text will be split
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Hello every body, to day is <em>a sunny day<em></p>
<button onclick="unwrapElement()">Do unwrap</button>
I have a body of text which is about 12 paragraphs long... inside the text are a couple of blockquotes which I would like to automatically highlight and rearrange throughout the document (every 3rd paragraph) and afterwards removing the blockquote so that the text appears twice. Once as a highlighted snippet in an automatically ordered position, and second in the original location without the block...
It sort of works, but I feel like I'm missing something because it doesn't follow the order. I would think it'd be (3,6,9, etc.) but it seems to be thrown off by something?
jQuery ->
content = $('article.city-review div')
content.find('blockquote').each (index) ->
line_space = (index+1)*3
quote_tag = '<span class=\"quote_left\">'+$(this).text()+'</span>'
content.find('p:nth-child('+line_space+')').prepend(quote_tag)
$(this).contents().unwrap().wrap('<p></p>')
UPDATE:
Input looks like:
<p>Text</p>
<p>More Text</p>
<p>Text</p>
<p>More Text</p>
<blockquote>Text</blockquote>
<p>Text</p>
<p>More Text</p>
<blockquote><p>Sometimes these appear</p></blockquote>
The output gives me empty p tags <p></p> and nested p tags <p><p>Something</p></p>
jQuery ->
content = $('article.city-review div')
content.find('blockquote').each (index) ->
position = [2,6,10]
line_space = position[index]
text = $(this).text()
quote_tag = '<span class=\"quote_left\">'+text+'</span>'
content.find('p:nth-child('+line_space+')').after(quote_tag)
$(this).replaceWith('<p>'+text+'</p>')
Making an array is an easy way to test out the spacing and unless you need an unlimited length could work. Remove the unwrap and use replacewith, maybe simpler for you and after instead of prepend unless you want the new quotes inside another tag.
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>' );
This is probably a fairly easy question, but I'm new to JavaScript and jquery....
I have a website with a basic show/hide toggle. The show/hide function I'm using is here:
http://andylangton.co.uk/articles/javascript/jquery-show-hide-multiple-elements/
So here's my question..... I would really like the first 5-10 words of the toggled section to always be visible. Is there some way I can change it so that it doesn't hide the entire element, but hides all but the first few words of the element?
Here's a screenshot of what I would like it to do:
http://answers.alchemycs.com/mobile/images/capture.jpg
There are many different implementation possibilities:
You can divide the contents up into the first part and the second part (two separate spans or divs inside your main object) and hide only the child object that represents the second part, not hide the parent object.
Rather than hide the object at all, you can set its height to only show the first part (with overflow: hidden)
Change the contents of the main object to only have the first part as the contents (requires you to maintain the full contents somewhere else so you can restore it when expanded again).
Here's a working example of option 1: http://jsfiddle.net/jfriend00/CTzsP/.
You'd need to either:
Put in a span/etc. after the first n words, and only hide that part, or
Change the viewable region, or
Replace or toggle the span/etc. with the "collapsed" view.
The last is a bit more customizable; using two separate elements allows trivial games to be played (showing an image, for example, like a little curly arrow) without modifying adding/removing DOM elements.
I tend towards the last because it's simple and obvious, but that's a personal preference, and really isn't as true as it used to be.
You can do some plugin authoring,I did a sample demo here ,based on your screenshot
<div class="toggle">ShowHide</div>
<div class="content">some content some content some content some content some content <br/> some content some content some content </div>
<div class="toggle">ShowHide</div>
<div class="content">some content some content some content some content some content <br/> some content some content some content </div>
here is javascript/jquery code
jQuery.fn.myToggle = function(selector, count) {
var methods = {
toggle: function(selector, count) {
if ($(selector).is(':visible')) {
var span = $('<span>');
span.text($(selector).text().substr(0, count) + "...");
span.insertAfter($(selector));
$(selector).hide();
}
else {
$(selector).show();
$(selector).next('span').hide();
}
}
};
$(this).each(function() {
methods.toggle($(this).next(selector), count);
$(this).click(function(evt) {
methods.toggle($(this).next(selector), count);
});
});
};
$(function() {
$('.toggle').myToggle('.content', 3);
});
Here is a solution using css properties only instead of mangling the dom.
http://jsfiddle.net/AYre3/4/
Now if you want some sort of animation happening as well you'll probably need to do a bit of measurement along the way.