Set anchor name with execCommand - javascript

I know how to set an <a /> tag with the href attribute in a contenteditable like this:
execCommand("CreateLink", false, "#jumpmark");
which will result in
selection
However I cannot figure out how to set an anchor name instead of the href.
This is my desired result:
<a name="jumpmark">selection</a>
Can anyone help me?
Side notes: I am using jQuery and Rangy as libraries, however I would prefer a solution that works directly with execCommand.
Update: Here's a jsfiddle: http://jsfiddle.net/fjYHr/ Select some text and click the button. All I want is that with the button click a link is inserted with a name attribute set instead of the href.

You could use something like the following, which is adapted from the pasteHtmlAtCaret() function from this answer of mine:
Demo: http://jsfiddle.net/F8Zny/
Code:
function surroundSelectedText(element) {
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
element.appendChild( document.createTextNode(range.toString()) );
range.deleteContents();
range.insertNode(element);
// Preserve the selection
range = range.cloneRange();
range.setStartAfter(element);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
} else if (document.selection && document.selection.type != "Control") {
// IE < 9
var selRange = document.selection.createRange();
element.appendChild( document.createTextNode(selRange.text) );
selRange.pasteHTML(element.outerHTML);
}
}
If you must use document.execCommand() then you could use the InsertHTML command in non-IE browsers. However, IE does not support it.
document.execCommand("InsertHTML", false, '<a name="jumpmark">selection</a>');

I see you're using Rangy, but I don't how to use it at all. Before I realized what Rangy was, I looked up how to get the current selection. I found a function that gets it and replaces it with a passed in value. I ended up modfiying it, but here it is:
http://jsfiddle.net/fjYHr/1/
$(document).ready(function () {
$("#setlink").click(function () {
replaceSelectedText("jumplink");
});
});
function replaceSelectedText(nameValue) {
var sel, sel2, range;
if (window.getSelection) {
sel = window.getSelection();
sel2 = ""+sel; // Copy selection value
if (sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
var newA = document.createElement("a");
newA.name = nameValue;
newA.innerHTML = sel2;
range.insertNode(newA);
}
} else if (document.selection && document.selection.createRange) {
// Not sure what to do here
range = document.selection.createRange();
var newA = "<a name='" + nameValue.replace(/'/g, "") + "'>" + range.text + "</a>";
range.text = newA;
}
}
Notice how I store the original current selection, then replace it with an <a> element that gets its name set with the passed-in value.
As for the document.selection part (which seems to be used by IE < 9), I'm not 100% sure that the code I provided will work (actually allow HTML in the selection, and not escaping it). But it's my attempt :)

As you've seen execCommand is rather limited in the attributes you can set, as such you cannot set the name attribute using it - only the href.
As you have jQuery set as a tag, you can use that as an alternative:
var $a = $('<a></a>').attr('name', 'jumpmark').appendTo('body');
Update
I need to work on the current selection. Specifically I don't have a jQuery object that I can append to, meaning I don't have a DOM node that I can work on
In this case use a plugin such as Rangy to get the selection which you can then amend with jQuery as required.

Related

How can I show an object at the location where I select text?

I want to create a CMS like wordpress. In my text editor I want the user to be able to create a hyperlink via a button click. But I don't want to show an alert so the user can input the url but a div shown under the selected word/sentence inside or over the text area with an text input. How do I get the location of the selected word?
I already tried to append a textnode to it like this:
window.getSelection().appendChild(document.createTextNode("testing"));
but I get an error, that .appendChild() is not a function.
$('#btnLink').click(function() {
window.getSelection().appendChild(document.createTextNode("testing"));
})
I expect the textnode is appended to the selected word, but it doesnt work
The getSelection() method will not return a node to append text to.
I've used some code from a different answer (added below the code) to achieve what you're asking.
$('#btnLink').click(function() {
var elm = getRange();
var div = document.createElement("div");
div.appendChild( document.createElement("input") );
elm.collapse(false);
elm.insertNode(div);
});
function getRange() {
var range, sel, container;
if (document.selection) {
range = document.selection.createRange();
range.collapse(isStart);
return range.parentElement();
} else {
sel = window.getSelection();
if (sel.getRangeAt) {
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0);
}
} else {
// Old WebKit
range = document.createRange();
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(sel.focusNode, sel.focusOffset);
// Handle the case when the selection was selected backwards (from the end to the start in the document)
if (range.collapsed !== sel.isCollapsed) {
range.setStart(sel.focusNode, sel.focusOffset);
range.setEnd(sel.anchorNode, sel.anchorOffset);
}
}
if (range) {
return range;
}
}
}
This code is copied and altered from How can I get the DOM element which contains the current selection? to demonstrate the use for this specific question.
A JSFiddle: https://jsfiddle.net/zuvq9nyc/5/
try this:
$('#btnLink').click(function() {
window.getSelection.append(document.createTextNode('testing'));
})
.appendchild() is a javascript function, jquery can't use it. use .append() instead and use .createTextNode() inside it.

How to find if a HTMLElement is enclosed in Selected text

Is it possible to find out if an HTMLElement is totally enclosed within the selection?
I have a scenario where user selects some text in a HTML editor and applies some custom style from a list. Now I need to change the class attribute of each span element that is enclosed in that selection and surrounding the selection with a new span with the selected style.
Am able to find out if a particular span element is in selection by using DOM Range's compareBoundaryPoints method in firefox and safari but it will not work for IE.
Is there any way to find out if an element is totally enclosed with in the selected range for IE?
Thanks
Kapil
As #standardModel says, Rangy gives you full* DOM Range support in IE and has a helpful getNodes() method that you could use:
var sel = rangy.getSelection();
if (sel.rangeCount) {
var range = sel.getRangeAt(0);
var spans = range.getNodes([1], function(node) {
return node.nodeName.toLowerCase() == "span" && range.containsNode(node);
});
// Do stuff with spans here
}
If you'd rather not use something as bulky as Rangy, the following function will tell you if an element is completely selected:
function isSelected(el) {
if (window.getSelection) {
var sel = window.getSelection();
var elRange = document.createRange();
elRange.selectNodeContents(el);
for (var i = 0, range; i < sel.rangeCount; ++i) {
range = sel.getRangeAt(i);
if (range.compareBoundaryPoints(range.START_TO_START, elRange) <= 0
&& range.compareBoundaryPoints(range.END_TO_END, elRange) >= 0) {
return true;
}
}
} else if (document.selection && document.selection.type == "Text") {
var textRange = document.selection.createRange();
var elTextRange = textRange.duplicate();
elTextRange.moveToElementText(el);
return textRange.inRange(elTextRange);
}
return false;
}
jsFiddle example: http://jsfiddle.net/54eGr/1/
(*) Apart from handling Range updates under DOM mutation
You may want to take a look at Rangy. This makes xbrowser Ranges and Selections a lot easier.

javascript replace selection all browsers

Is there a simple js function I can use to replace the current document's selection with some html of mine?
For instance say the document contains a <p>AHAHAHA</p> somewhere and user selects the 1st "ha" text chunk.
Now I want to replace this with something like: <span><font color="red">hoho</font></span>
When I google for *javascript replace selection * I can't get a simple straightforward answer!
Yes. The following will do it in all major browsers, with an option to select the inserted content afterwards as requested in the comments (although this part is not implemented for IE <= 8):
Live demo: http://jsfiddle.net/bXsWQ/147/
Code:
function replaceSelection(html, selectInserted) {
var sel, range, fragment;
if (typeof window.getSelection != "undefined") {
// IE 9 and other non-IE browsers
sel = window.getSelection();
// Test that the Selection object contains at least one Range
if (sel.getRangeAt && sel.rangeCount) {
// Get the first Range (only Firefox supports more than one)
range = window.getSelection().getRangeAt(0);
range.deleteContents();
// Create a DocumentFragment to insert and populate it with HTML
// Need to test for the existence of range.createContextualFragment
// because it's non-standard and IE 9 does not support it
if (range.createContextualFragment) {
fragment = range.createContextualFragment(html);
} else {
// In IE 9 we need to use innerHTML of a temporary element
var div = document.createElement("div"), child;
div.innerHTML = html;
fragment = document.createDocumentFragment();
while ( (child = div.firstChild) ) {
fragment.appendChild(child);
}
}
var firstInsertedNode = fragment.firstChild;
var lastInsertedNode = fragment.lastChild;
range.insertNode(fragment);
if (selectInserted) {
if (firstInsertedNode) {
range.setStartBefore(firstInsertedNode);
range.setEndAfter(lastInsertedNode);
}
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if (document.selection && document.selection.type != "Control") {
// IE 8 and below
range = document.selection.createRange();
range.pasteHTML(html);
}
}
Example:
replaceSelection('<span><font color="red">hoho</font></span>', true);
You can use the Rangy library
http://code.google.com/p/rangy/
You can then do
var sel = rangy.getSelection();
var range = sel.getRangeAt(0);
range.deleteContents();
var node = range.createContextualFragment('<span><font color="red">hoho</font></span>');
range.insertNode(node);

Change CSS of selected text using Javascript

I'm trying to make a JavaScript bookmarklet that will act as a highlighter, changing the background of selected text on a webpage to yellow when the bookmarklet is pressed.
I'm using the following code to get the selected text, and it works fine, returning the correct string
function getSelText() {
var SelText = '';
if (window.getSelection) {
SelText = window.getSelection();
} else if (document.getSelection) {
SelText = document.getSelection();
} else if (document.selection) {
SelText = document.selection.createRange().text;
}
return SelText;
}
However, when I created a similar function to change the CSS of the selected text using jQuery, it isn't working:
function highlightSelText() {
var SelText;
if (window.getSelection) {
SelText = window.getSelection();
} else if (document.getSelection) {
SelText = document.getSelection();
} else if (document.selection) {
SelText = document.selection.createRange().text;
}
$(SelText).css({'background-color' : 'yellow', 'font-weight' : 'bolder'});
}
Any ideas?
The easiest way to do this is to use execCommand(), which has a command to change the background colour in all modern browsers.
The following should do what you want on any selection, including ones spanning multiple elements. In non-IE browsers it turns on designMode, applies a background colour and then switches designMode off again.
UPDATE
Fixed in IE 9.
function makeEditableAndHighlight(colour) {
var range, sel = window.getSelection();
if (sel.rangeCount && sel.getRangeAt) {
range = sel.getRangeAt(0);
}
document.designMode = "on";
if (range) {
sel.removeAllRanges();
sel.addRange(range);
}
// Use HiliteColor since some browsers apply BackColor to the whole block
if (!document.execCommand("HiliteColor", false, colour)) {
document.execCommand("BackColor", false, colour);
}
document.designMode = "off";
}
function highlight(colour) {
var range, sel;
if (window.getSelection) {
// IE9 and non-IE
try {
if (!document.execCommand("BackColor", false, colour)) {
makeEditableAndHighlight(colour);
}
} catch (ex) {
makeEditableAndHighlight(colour)
}
} else if (document.selection && document.selection.createRange) {
// IE <= 8 case
range = document.selection.createRange();
range.execCommand("BackColor", false, colour);
}
}
Here is a crude example of how it could work. As Zack points out you'll need to be aware of cases where the selection spans multiple elements. This isn't intended to be used as-is, just something to help get ideas flowing. Tested in Chrome.
var selection = window.getSelection();
var text = selection.toString();
var parent = $(selection.focusNode.parentElement);
var oldHtml = parent.html();
var newHtml = oldHtml.replace(text, "<span class='highlight'>"+text+"</span>");
parent.html( newHtml );
To make the highlight stick permanently, I believe you are going to have to wrap the selection in a new DOM element (span should do), to which you can then attach style properties. I don't know if jQuery can do that for you. Keep in mind that selections can span element boundaries, so in the general case you're going to have to inject a whole bunch of new elements
Have a look at a little example i made at http://www.jsfiddle.net/hbwEE/3/
It does not take into account selections that span multiple elements..
(IE will do but will mess the html a bit ..)
In Firefox, you can use the ::-moz-selection psuedo-class.
In Webkit, you can use the ::selection pseudo-class.
I like Tim's answer, it's clean and fast. But it also shuts down the doors to doing any interactions with the highlights.
Inserting inline elements directly around the texts is a bad choice, as they broke the text flow and mess things up in complex situations,
So I suggest a dirty hack that
calculates the absolute layout of each line of selected text (no matter where they are),
then insert colored, semi-transparent inline-block elements in the end of the document body.
This chrome extension is an example of how this can be done.
It uses API from this library to get the absolute layout of each selected line.

how to modify the document selection in javascript?

I wanna modify the document selection (user currently selected by mouse or keyboard), how to do it in a cross browser way?
I have not worked with text selection enough to provide real help, but what you are trying to do can be done. You will want to look into the following two functions:
createRange() MSDN | MDC
getRangeAt() MDC
I know it can be implemented cross browser. You can see some of it in action here:
http://fuelyourcoding.com/a-few-strategies-for-using-javascript/
By scrolling to the bottom and clicking the Elephant Icon, which uses the Evernote script. However, my script first selects the main content area (you will see it flash orange) and then it deselects once the capture is made.
Here is a mini jQuery plugin that does it. It was adapted by me from some site, and like the comments say, I feel horrible for not remembering. Its really important to note I adapted it to jQuery, but the code came from some site where they explained how to do it:
// Adapted this from somewhere. Feel horrible for not remembering.
$.fn.autoSelect = function(){
var selectTarget = this[0]; // Select first element from jQuery collection
if(selectTarget != null) {
if(selectTarget.tagName == 'TEXTAREA' || (selectTarget.tagName == "INPUT" && selectTarget.type == "text")) {
selectTarget.select();
} else if(window.getSelection) { // FF, Safari, Opera
var sel = window.getSelection();
var range = document.createRange();
range.selectNode(selectTarget);
sel.removeAllRanges();
sel.addRange(range);
} else { // IE
document.selection.empty();
var range = document.body.createTextRange();
range.moveToElementText(selectTarget);
range.select();
};
};
return this; // Don't break the chain
};
It seems this script is a few places online, but here is another variation on it
As an example, and the easiest one, let's say you want to move the user's selection to contain the contents of an element. The following will work in all major browsers:
function selectElementContents(el) {
var body = document.body, range, sel;
if (body.createTextRange) {
range = body.createTextRange();
range.moveToElementText(el);
range.select();
} else if (document.createRange && window.getSelection) {
range = document.createRange();
range.selectNodeContents(el);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
}
selectElementContents( document.getElementById("someElement") );

Categories