I am trying to but together a highlighting menu that hovers over a selection with the following properties:
It should appear when a selection is made.
It should disappear when the selection is destroyed.
All that works quite nicely except for one thing: If an existing selection gets clicked the selection disappears and so should the hovering menu. But for whatever reason it doesn't.
When you click outside of an existing selection the selection type changes to 'caret' or to 'none' if you click on that very selection. So I tried setting the visibility of the menu according to the type. The problem is though that although the type of the selection appears to change in the object you get by window.getSelection(), it does not if you try to get the type from the object.
I put this jsfiddle together to illustrate the problem. https://jsfiddle.net/nxo2d7ew/1/
var el = document.getElementById("simple-text");
el.addEventListener("mouseup", placeDiv, false);
function placeDiv(x_pos, y_pos) {
var sel = window.getSelection();
var position = sel.getRangeAt(0).getBoundingClientRect();
// console.log(sel)
// console.log(position)
var highlighter = document.getElementById('highlighter').offsetWidth
var d = document.getElementById('highlighter');
d.style.left = position.left+position.width*0.5-highlighter*0.5 +'px';
d.style.top = position.top-50 +'px';
// console.log('sel.type: ' + sel.type)
var test = window.getSelection()
console.log(test) // returns an object with "type: None"
console.log(test.type) //returns "Range"
if (test.type !== 'Range') {
d.style.visibility = 'hidden';
}
else {
d.style.visibility = 'visible';
}
var sel = ''
}
Thank you :-)
The real change to selection doesn't really happen on mouseup event. There is another step afterwards that changes the selection, so when mouseup is fired, the selection hasn't changed yet. What you see in your console is not the exact state of the selection object on the mouseup event.
I'm not sure there's a cross-browser way to have access to the real selection change event, there's a selectionchange event in Chrome, and supposedly in IE (but I couldn't test it). But in Firefox it's not available. That said, the type property you're using to test if it's an empty selection doesn't seem to work on Firefox either. But you could use isCollapsed.
One way you can maybe solve the problem, though not the most elegant, is using a timeout, you only need a few milliseconds for the selection to update, then your logic will work - using isCollapsed to make it work on Firefox. Like this:
setTimeout(function(){
var test = window.getSelection()
console.log(test) // returns an object with "type: None"
console.log(test.type) //returns "Range"
if (test.isCollapsed) {
d.style.visibility = 'hidden';
}
else {
d.style.visibility = 'visible';
}}, 25);
https://jsfiddle.net/q1f4xfw9/6/
Or with selectionchange event in Chrome, you move the hide condition into this handler. Like this:
document.addEventListener("selectionchange", function () {
var d = document.getElementById('highlighter');
var test = window.getSelection()
if (test.type !== 'Range') {
d.style.visibility = 'hidden';
}
});
https://jsfiddle.net/q1f4xfw9/5/
EDIT:
There's another way to solve the problem, you could remove selection on mouse down using removelAllRanges. Then the selection change event would be triggered before the mouseup. Up to you to see if that little change in functionality works with what you want. Like this:
el.addEventListener("mousedown", function(e){
window.getSelection().removeAllRanges()
}, false);
https://jsfiddle.net/q1f4xfw9/8/
Related
So right now, I can dynamically create elements (2 rows of 12 blocks) and when I click on an individual block, I can change the color of it as well.
However, I am having one problem. When I click on a block to have its color changed, the color picker will pop up beside it, no issues at all. When I add a new set of rows and try to color the same block number, it will replace the color of the block from the previous row.
For example, if I color the 12th block in the first row, then add 2 new sets of rows and click on the same block in the second set, it will act as if I'm clicking on the previous set's block. I am using https://bgrins.github.io/spectrum/ as my colorPicker
Here is a link to what I have done so far:
http://codepen.io/anon/pen/bwBRmw
var id_num = 1;
var picker = null;
$(function () {
$(document).on('click', ".repeat", function (e) {
e.preventDefault();
var $self = $(this);
var $parent = $self.parent();
if($self.hasClass("add-bottom")){
$parent.after($parent.clone(true).attr("id", "repeatable" + id_num));
id_num = id_num + 1;
//picker = null;
} else {
$parent.before($parent.clone(true).attr("id", "repeatable" + id_num));
id_num = id_num + 1;
//picker = null;
}
});
});
$(".container").on("click", "a", function(e) {
var self = this;
console.log(this.id)
console.log(this)
$(self).spectrum({
color: "#f00",
change: function(color) {
$(self).css('background-color',color.toHexString());
}
});
e.stopPropagation();
})
The problem seems to be that you are cloning elements which already have the colorpicker events bound.
EDIT: I think I've managed to work around the problem by changing your use of jQuery's clone(). If you tell it to clone without including events (omitting the first parameter to clone() which defaults to false, the DOM objects will be created without the colorpicker pointing at the old ones.
Here's an example that I think is doing what you are looking for. I've just removed the true params for clone(). No changes to HTML or CSS.
I've been trying to create an advanced text input where users can write hashtags.
The input is a div with contenteditable set true, and the hashtags should be child nodes as I'd allow users to put space inside the hashtags.
My problem is that on some browsers I can not set the focus on the hashtag's child node as the user types. On Chrome/Linux and Safari/OSX it seems to work well, but on Firefox and Chrome/OSX setting the focus don't seem to work. (I haven't got to Windows yet.)
var $elComposer = $('#composer');
var InsertTagPair = function (tagtype) {
var newTag = document.createElement (tagtype);
$(newTag).attr ('contenteditable', 'true');
$(newTag).attr ('class', 'tag');
$elComposer.off ('keypress');
if (window.getSelection) {
var selection = window.getSelection();
if (selection.getRangeAt && selection.rangeCount) {
var range = selection.getRangeAt (0);
range.deleteContents ();
range.insertNode (newTag);
range.selectNodeContents (newTag);
range.collapse (false);
selection.removeAllRanges ();
selection.addRange (range);
};
};
newTag.focus ();
return newTag;
};
var ComposerOnKeyPressed = function (event) {
if (event.charCode == 35) { // #
var contextTag = InsertTagPair ('span');
$elComposer.attr ('contenteditable', 'false');
return false;
};
return event;
};
$elComposer.on ('keypress', ComposerOnKeyPressed);
The above code is the essential part that's not working. See it here in action:
JSFiddle - http://jsfiddle.net/cja963ym/1/
To see a more complete version of the composer that makes more sense see this instead:
JSFiddle - http://jsfiddle.net/g_borgulya/ur6zk32s/15/
Symptom: on Firefox if you type in the editable div and press '#', you manually have to click it by mouse or move the focus with tab to be able to edit the hashtag, while on other platforms it gets the focus when I call newTag.focus() on the element.
I don't know how to move on, even how to debug this problem.
Adding newTag.focus() in the "#" handler made it work in Chrome and Firefox (Win) (it didn't before, but did on IE11, just wanted to let you know)
Fiddle fork : http://jsfiddle.net/ekxo4ra3/
Does that look OK for what you wanted ?
I have a paragraph of text in which the user may place a "pin" to mark a position. Once a pin has been placed, I would like to allow the user to move its position by dragging it to a new location in the paragraph. This is simple to do with block elements, but I have yet to see a good way to do it with inline elements. How might I accomplish this?
I have already implemented it using window.selection as a way to find the cursor's location in the paragraph, but it is not as smooth as I would like.
As a note, I am using the Rangy library to wrap the native Range and Selection functionality, but it works the same way as the native functions do.
Here is the code:
$(document).on("mousedown", '.pin', function () {
//define what a pin is
var el = document.createElement("span");
el.className = "pin";
el.id = "test";
//make it contain an empty space so we can color it
el.appendChild(document.createTextNode("d"));
$(document).on("mousemove", function () {
//get the current selection
var selection = rangy.getSelection();
//collapse the selection to either the front
//or back, since we do not want the user to see it.
if (selection.isBackwards()) {
selection.collapseToStart();
} else {
selection.collapseToEnd();
}
//remove the old pin
$('.pin').remove();
//place the new pin at the current selection
selection.getAllRanges()[0].insertNode(el);
});
//remove the handler when the user has stopped dragging it
$(document).on("mouseup", function () {
$(document).off("mousemove");
});
});
And here is a working demo: http://jsfiddle.net/j1LLmr5b/22/ .
As you can see, it works(usually), but the user can see the selection being made. Have any ideas on how to move the span without showing the selection highlight? I will also accept an alternate method that does not use the selection at all. The goal is to allow movement of the span as cleanly as possible.
You can do this using ranges instead using code similar to this answer. Unfortunately the code is a bit longer than ideal because IE hasn't yet implemented document.caretPositionFromPoint(). However, the old proprietary TextRange object, still present in IE 11, comes to the rescue.
Here's a demo:
http://jsfiddle.net/j1LLmr5b/26/
Here's the relevant code:
var range, textRange, x = e.clientX, y = e.clientY;
//remove the old pin
$('.pin').remove();
// Try the standards-based way first
if (document.caretPositionFromPoint) {
var pos = document.caretPositionFromPoint(x, y);
range = document.createRange();
range.setStart(pos.offsetNode, pos.offset);
range.collapse();
}
// Next, the WebKit way
else if (document.caretRangeFromPoint) {
range = document.caretRangeFromPoint(x, y);
}
// Finally, the IE way
else if (document.body.createTextRange) {
textRange = document.body.createTextRange();
textRange.moveToPoint(x, y);
var spanId = "temp_" + ("" + Math.random()).slice(2);
textRange.pasteHTML('<span id="' + spanId + '"> </span>');
var span = document.getElementById(spanId);
//place the new pin
span.parentNode.replaceChild(el, span);
}
if (range) {
//place the new pin
range.insertNode(el);
}
Try this my friend
el.appendChild(document.createTextNode("d"));
You have create empty span tag that's why you found empty.
add after
el.id = "test";
this
var value = $('.pin').text();
$(el).text(value);
You can hide selection with css
::selection {color:red;background:yellow;}
::-moz-selection {color:red;background:yellow;}
that's all how i can help for a now
I have got this working with the start point as a span, but I want to have the form still function if javascript is disabled in the browser this is how I had it working originally. I'm still very new to javascript, can someone lend a hand please.
window.onload = function() {
document.getElementById('container').onclick = function(event) {
var span, input, text;
// Get the event (handle MS difference)
event = event || window.event;
// Get the root element of the event (handle MS difference)
span = event.target || event.srcElement;
// If it's a span...
if (span && span.tagName.toUpperCase() === "SPAN") {
// Hide it
span.style.display = "none";
// Get its text
text = span.innerHTML;
// Create an input
input = document.createElement("input");
input.type = "text";
input.size = Math.max(text.length / 4 * 3, 4);
span.parentNode.insertBefore(input, span);
// Focus it, hook blur to undo
input.focus();
input.onblur = function() {
// Remove the input
span.parentNode.removeChild(input);
// Update the span
span.innerHTML = input.value;
// Show the span again
span.style.display = "";
};
}
};
};
Best way to do this would be to show the input first, then quickly swap it out when the page loads, then swap it back when the user clicks.
You might also consider using the form element the whole time, but just changing CSS classes on it to make it look like normal text. This would make your UI cleaner and easier to maintain in the future.
Then just put the input fields there from the start, and hide them with a script that runs when the form has loaded. That way all the fields will be visible if Javascript is not supported.
I think your best option would be to wrap a form with noscript tags which will fire when Javascript is disabled in a browser. If they display even while in the noscript tags then just set them as not visible with Javascript.
if you have jQuery, something like this should work.
function makeElementIntoClickableText(elm){
$(elm).parent().append("<div onClick='switchToInput(this);'>"+ elm.value +"</div>");
$(elm).hide();
}
function switchToInput(elm){
$(elm).prev().prev().show();
$(elm).hide();
}
makeElementIntoClickableText($("input")[0]);
use the readonly attribute in the input elements:
<input type="text" readonly />
And then remove that attribute with JavaScript in the onclick event handler, reassigning it on blur:
var inputs = document.getElementsByTagName('input');
for (i=0; i < inputs.length; i++) {
inputs[i].setAttribute('readonly',true);
inputs[i].onclick = function(){
this.removeAttribute('readonly');
};
inputs[i].onblur = function(){
this.setAttribute('readonly',true);
};
}
JS Fiddle demo.
This question has been asked in a few different formats but I can't get any of the answers to work in my scenario.
I am using jQuery to implement command history when user hits up/down arrows. When up arrow is hit, I replace the input value with previous command and set focus on the input field, but want the cursor always to be positioned at the end of the input string.
My code, as is:
$(document).keydown(function(e) {
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
var input = self.shell.find('input.current:last');
switch(key) {
case 38: // up
lastQuery = self.queries[self.historyCounter-1];
self.historyCounter--;
input.val(lastQuery).focus();
// and it continues on from there
How can I force the cursor to be placed at the end of 'input' after focus?
Looks like clearing the value after focusing and then resetting works.
input.focus();
var tmpStr = input.val();
input.val('');
input.val(tmpStr);
It looks a little odd, even silly, but this is working for me:
input.val(lastQuery);
input.focus().val(input.val());
Now, I'm not certain I've replicated your setup. I'm assuming input is an <input> element.
By re-setting the value (to itself) I think the cursor is getting put at the end of the input. Tested in Firefox 3 and MSIE7.
Hope this help you:
var fieldInput = $('#fieldName');
var fldLength= fieldInput.val().length;
fieldInput.focus();
fieldInput[0].setSelectionRange(fldLength, fldLength);
Chris Coyier has a mini jQuery plugin for this which works perfectly well: http://css-tricks.com/snippets/jquery/move-cursor-to-end-of-textarea-or-input/
It uses setSelectionRange if supported, else has a solid fallback.
jQuery.fn.putCursorAtEnd = function() {
return this.each(function() {
$(this).focus()
// If this function exists...
if (this.setSelectionRange) {
// ... then use it (Doesn't work in IE)
// Double the length because Opera is inconsistent about whether a carriage return is one character or two. Sigh.
var len = $(this).val().length * 2;
this.setSelectionRange(len, len);
} else {
// ... otherwise replace the contents with itself
// (Doesn't work in Google Chrome)
$(this).val($(this).val());
}
// Scroll to the bottom, in case we're in a tall textarea
// (Necessary for Firefox and Google Chrome)
this.scrollTop = 999999;
});
};
Then you can just do:
input.putCursorAtEnd();
Ref: #will824 Comment, This solution worked for me with no compatibility issues. Rest of solutions failed in IE9.
var input = $("#inputID");
var tmp = input.val();
input.focus().val("").blur().focus().val(tmp);
Tested and found working in:
Firefox 33
Chrome 34
Safari 5.1.7
IE 9
What about in one single line...
$('#txtSample').focus().val($('#txtSample').val());
This line works for me.
2 artlung's answer:
It works with second line only in my code (IE7, IE8; Jquery v1.6):
var input = $('#some_elem');
input.focus().val(input.val());
Addition: if input element was added to DOM using JQuery, a focus is not set in IE. I used a little trick:
input.blur().focus().val(input.val());
I know this answer comes late, but I can see people havent found an answer. To prevent the up key to put the cursor at the start, just return false from the method handling the event. This stops the event chain that leads to the cursor movement. Pasting revised code from the OP below:
$(document).keydown(function(e) {
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
var input = self.shell.find('input.current:last');
switch(key) {
case 38: // up
lastQuery = self.queries[self.historyCounter-1];
self.historyCounter--;
input.val(lastQuery).focus();
// HERE IS THE FIX:
return false;
// and it continues on from there
I use code below and it works fine
function to_end(el) {
var len = el.value.length || 0;
if (len) {
if ('setSelectionRange' in el) el.setSelectionRange(len, len);
else if ('createTextRange' in el) {// for IE
var range = el.createTextRange();
range.moveStart('character', len);
range.select();
}
}
}
It will be different for different browsers:
This works in ff:
var t =$("#INPUT");
var l=$("#INPUT").val().length;
$(t).focus();
var r = $("#INPUT").get(0).createTextRange();
r.moveStart("character", l);
r.moveEnd("character", l);
r.select();
More details are in these articles here at SitePoint, AspAlliance.
like other said, clear and fill worked for me:
var elem = $('#input_field');
var val = elem.val();
elem.focus().val('').val(val);
set the value first. then set the focus. when it focuses, it will use the value that exists at the time of focus, so your value must be set first.
this logic works for me with an application that populates an <input> with the value of a clicked <button>. val() is set first. then focus()
$('button').on('click','',function(){
var value = $(this).attr('value');
$('input[name=item1]').val(value);
$('input[name=item1]').focus();
});
I have found the same thing as suggested above by a few folks. If you focus() first, then push the val() into the input, the cursor will get positioned to the end of the input value in Firefox,Chrome and IE. If you push the val() into the input field first, Firefox and Chrome position the cursor at the end, but IE positions it to the front when you focus().
$('element_identifier').focus().val('some_value')
should do the trick (it always has for me anyway).
At the first you have to set focus on selected textbox object and next you set the value.
$('#inputID').focus();
$('#inputID').val('someValue')
function focusCampo(id){
var inputField = document.getElementById(id);
if (inputField != null && inputField.value.length != 0){
if (inputField.createTextRange){
var FieldRange = inputField.createTextRange();
FieldRange.moveStart('character',inputField.value.length);
FieldRange.collapse();
FieldRange.select();
}else if (inputField.selectionStart || inputField.selectionStart == '0') {
var elemLen = inputField.value.length;
inputField.selectionStart = elemLen;
inputField.selectionEnd = elemLen;
inputField.focus();
}
}else{
inputField.focus();
}
}
$('#urlCompany').focus(focusCampo('urlCompany'));
works for all ie browsers..
Here is another one, a one liner which does not reassign the value:
$("#inp").focus()[0].setSelectionRange(99999, 99999);
function CurFocus()
{
$('.txtEmail').focus();
}
function pageLoad()
{
setTimeout(CurFocus(),3000);
}
window.onload = pageLoad;
The answer from scorpion9 works. Just to make it more clear see my code below,
<script src="~/js/jquery.js"></script>
<script type="text/javascript">
$(function () {
var input = $("#SomeId");
input.focus();
var tmpStr = input.val();
input.val('');
input.val(tmpStr);
});
</script>
var prevInputVal = $('#input_id').val();
$('#input_id').val('').focus().val(prevInputVal)
Store input previous value in a variable -> empty input value -> focus input -> reassign original value SIMPLE !
It will focus with mouse point
$("#TextBox").focus();