I'm making a spreadsheet where double-clicking a cell brings up the 'edit' control, gives it focus, and sets up an onblur event handler to hide the 'edit' control and set the value of the cell to be that of the control once it loses focus. The assumed flow is the user double-clicks to bring up the edit control, selects/enters a value, then clicks/tabs to something else, thus moving focus to another element and triggering the edit control's onblur handler.
It works just fine, except with SELECT tags in IE 10 and 11 (Chrome and FF work fine): Every time I click on the carat to drop the drop-down, the SELECT loses focus and triggers the onblur handler, which then hides the edit control, thus preventing me from editing a value.
Does anyone know why this is happening or how to fix it?
I found this blog post which describes the problem and proposes a solution of adding a background image to the SELECT, but that didn't seem to fix the problem (or I did it wrong).
Below is the code where I generate and insert the HTML for the edit control into the cell.
/**Changes the contents of the cell to be its editing control instead of plain text.
#method SetCellEditMode
#param {String} mappingId: The Id of the PropertyMapping representing cell to switch over to edit mode.*/
this.SetCellEditMode = function (mappingId)
{
if (this.Editing === true) return false;
var cell = this.GetCell(mappingId);
var tagType = null;
if (cell == null) return false;
var propInfo = cell.SourceObject.PropertyInfo;
if (propInfo.IsReadOnly === true) return false;
var innerHtml = null;
if (propInfo.PropertyType === SDCMS.Resources.PropertyType.Boolean)
{
innerHtml = makeBoolHTML(cell.SourceObject);
}
else if (propInfo.PropertyType === SDCMS.Resources.PropertyType.Enum)
{
innerHtml = makeEnumHTML(cell.SourceObject);
}
else if (propInfo.PropertyType === SDCMS.Resources.PropertyType.Number || propInfo.PropertyType === SDCMS.Resources.PropertyType.String)
{
innerHtml = makeStringEntryHTML(cell.SourceObject);
}
cell.Node[0].innerText = "";
cell.Node.html(innerHtml);
cell.Node[0].firstChild.focus();
this.Editing = true;
return true;
};
/**Makes the HTML for a boolean <select> control.
#method makeBoolHTML
#param {Object} propertyMapping: The SDCMS.Resources.PropertyMapping for which we are making an <select> control.*/
var makeBoolHTML = function (propertyMapping)
{
if (propertyMapping == null) return null;
var innerHtml = "<select mappingId=\"" + propertyMapping.Id + "\" onblur=\"SD_Click(event, 'SD_ChangeValue')\">" +
"<option>true</option>" +
"<option>false</option>" +
"</select>";
if (propertyMapping.PropertyValue === true)
{
innerHtml.replace("<option>true</option>", "<option selected=\"selected\">true</option>")
}
else
{
innerHtml.replace("<option>false</option>", "<option selected=\"selected\">false</option>")
}
return innerHtml;
};
Found a solution. Turns out it was all input/select type nodes were losing focus, and what was happening was that the nodes that were in a table would, when clicked, bubble the event from the control to the table cell, which would then get focus and cause blur() to trigger on the internal control. The solution was to hook up event handlers for onclick, onmousedown, and onmouseup (for good measure) that do nothing but preventDefault() and stopPropagation(). Once the event stopped propagating to the containing table cell, everything worked as it should.
Instead of calling focus directly call it using settimeout (), an interval of 1 to 10 ms should be enough.
Related
I'm working on a chrome extension and I want to do something with the text that is selected (highlighted by the user) on the page. For that, I need a way to remove the selected text, for example text inside an input field.
I found a way to "clear" the selected text, meaning it will be unselected:
Clear Text Selection with JavaScript But it doesn't seem to be what I'm looking for.
This just removes the highlighting from the text:
window.getSelection().empty();
I want to remove the text that is selected, if it's editable text. Is this possible with JavaScript?
You can use the deleteFromDocument method:
window.getSelection().deleteFromDocument()
This will immediately remove the selected content from the document, and as such also clear the selection.
As described formally in the MDN web docs:
The deleteFromDocument() method of the Selection interface deletes the selected text from the document's DOM.
If you'd like to be able to delete text from input elements instead, you need to use different APIs:
var activeEl = document.activeElement;
var text = activeEl.value;
activeEl.value = text.slice(0, activeEl.selectionStart) + text.slice(activeEl.selectionEnd);
Edit from me, Synn Ko: to cover input fields, textareas and contenteditables, use this:
var selection = window.getSelection();
var actElem = document.activeElement;
var actTagName = actElem.tagName;
if(actTagName == "DIV") {
var isContentEditable = actElem.getAttribute("contenteditable"); // true or false
if(isContentEditable) {
selection.deleteFromDocument();
}
}
if (actTagName == "INPUT" || actTagName == "TEXTAREA") {
var actText = actElem.value;
actElem.value = actText.slice(0, actElem.selectionStart) + actText.slice(actElem.selectionEnd);
}
I am trying to swap two cells using two separate click events in javascript. The problem is that the values stored by the first click event is overwritten by the second click event, and the console shows me that the StringAdjacent value stored for the second click event has been overwritten. This is my code:
//Listen to a Set of Click Events to Swap Cells
document.getElementById('board').addEventListener("click", function(e){
click1ID = event.target.id;
click1Class = event.target.className;
stringAdjacency1 = click1ID.replace('cell','')
console.log(stringAdjacency1);
document.getElementById('board').addEventListener("click", function(e){
click2ID = event.target.id;
click2Class = event.target.className;
stringAdjacency2 = click2ID.replace('cell','')
console.log(stringAdjacency2);
});
console.log(stringAdjacency1, stringAdjacency2);
});
function swapIds(click1ID, click1Class, click2ID, click2Class) {
//Are cells adjacent? If so, swap Cells
//Check the winning combinations to see if there's a new match;
//Swap cells;
});
Please help! Thank you.
You are adding a click event listener, which when fired by clicking the first cell causes another click event listener to be added. So when you click the second cell, it will fire the first event listener (overwriting the value), plus the second listener you added after the first one.
You only need to register a single listener who's function can handle the logic:
Something like this should work (didn't test):
var firstCell = null;
document.getElementById('board').addEventListener("click", function(e){
if(!firstCell) {
firstCell = e.target;
} else {
var secondCell = e.target;
// do whatever logic you want
// reset first cell
firstCell = null;
}
});
This will set firstCell to be the first cell clicked, then the second click it will no longer be null, so it will go into the else condition and you can do whatever you want. Then you'll reset firstCell so the entire interaction can be repeated.
I've made this tinymce fiddle to show what I say.
Highlight text in the editor, then click on the input text, highlight in tinyMCE is lost (obviously).
Now, I know it's not easy since both, the inline editor and the input text are in the same document, thus, the focus is only one. But is there any tinymce way to get like an "unfocused" highlight (gray color) whenever I click in an input text?
I'm saying this because I have a customized color picker, this color picker has an input where you can type in the HEX value, when clicking OK it would execCommand a color change on the selected text, but it looks ugly because the highlight is lost.
I don't want to use an iframe, I know that by using the non-inline editor (iframe) is one of the solutions, but for a few reasons, i can't use an iframe text editor.
Any suggestion here? Thanks.
P.S: Out of topic, does any of you guys know why I can't access to tinymce object in the tinyMCE Fiddle ? looks like the tinyMCE global var was overwritten by the tinymce select dom element of the page itself. I can't execute a tinyMCE command lol.
Another solution:
http://fiddle.tinymce.com/sBeaab/5
P.S: Out of topic, does any of you guys know why I can't access to
tinymce object in the tinyMCE Fiddle ? looks like the tinyMCE global
var was overwritten by the tinymce select dom element of the page
itself. I can't execute a tinyMCE command lol.
Well, you can access the tinyMCE variable and even execute commands.
this line is wrong
var colorHex = document.getElementById("colorHex")
colorHex contains input element, not value.
var colorHex = document.getElementById("colorHex").value
now it works ( neolist couldn't load, so I removed it )
http://fiddle.tinymce.com/DBeaab/1
I had to do something similar recently.
First off, you can't really have two different elements "selected" simultaneously. So in order to accomplish this you're going to need to mimic the browser's built-in 'selected text highlight'. To do this, you're going to have to insert spans into the text to simulate highlighting, and then capture the mousedown and mouseup events.
Here's a fiddle from StackOverflow user "fullpipe" which illustrates the technique I used.
http://jsfiddle.net/fullpipe/DpP7w/light/
$(document).ready(function() {
var keylist = "abcdefghijklmnopqrstuvwxyz123456789";
function randWord(length) {
var temp = '';
for (var i=0; i < length; i++)
temp += keylist.charAt(Math.floor(Math.random()*keylist.length));
return temp;
}
for(var i = 0; i < 500; i++) {
var len = Math.round(Math.random() * 5 + 3);
document.body.innerHTML += '<span id="'+ i +'">' + randWord(len) + '</span> ';
}
var start = null;
var end = null;
$('body').on('mousedown', function(event) {
start = null;
end = null;
$('span.s').removeClass('s');
start = $(event.target);
start.addClass('s');
});
$('body').on('mouseup', function(event) {
end = $(event.target);
end.addClass('s');
if(start && end) {
var between = getAllBetween(start,end);
for(var i=0, len=between.length; i<len;i++)
between[i].addClass('s');
alert('You select ' + (len) + ' words');
}
});
});
function getAllBetween(firstEl,lastEl) {
var firstIdx = $('span').index($(firstEl));
var lastIdx = $('span').index($(lastEl));
if(lastIdx == firstIdx)
return [$(firstEl)];
if(lastIdx > firstIdx) {
var firstElement = $(firstEl);
var lastElement = $(lastEl);
} else {
var lastElement = $(firstEl);
var firstElement = $(lastEl);
}
var collection = new Array();
collection.push(firstElement);
firstElement.nextAll().each(function(){
var siblingID = $(this).attr("id");
if (siblingID != $(lastElement).attr("id")) {
collection.push($(this));
} else {
return false;
}
});
collection.push(lastElement);
return collection;
}
As you can see in the fiddle, the gibberish text in the right pane stays highlighted regardless of focus elsewhere on the page.
At that point, you're going to have to apply your color changes to all matching spans.
Working on a project where I need to manually fill in input fields and textareas on various random websites. So far I've been doing "element.value = 'new value'" alongside "element.innerHTML = 'new value'", but for some websites there seem to be event listeners that aren't getting called when I do this.
So I figured I'd better call these manually, and added "element.onchange()" and a bunch of others (onkeypress, onkeydown, onkeyup), and also tried passing in the element into the function "element.onchange(element)". But neither of these seem to work for many websites.
As an example, go to http://www.facebook.com (logged out) and run:
javascript:var elements = document.getElementsByClassName('inputtext'); for(var i = 0; i < elements.length; i++) { var element = elements[i]; element.value = 'New Test Value'; element.innerHTML = 'New Test Value'; element.onkeypress; };
You should see all input boxes change value to "New Test Value", but the placeholder text is still in place (belongs to a different element). When you click on a input box on the page manually some function is getting called that removes this if there's text in the box.
So how do I make sure that any event listener attached to the element always gets called just as it does when we change value manually?
Hope this helps
var isCreatEvent = "createEvent" in document,
allEles = document.querySelectorAll('input'); // collect required elements.
for(var i = 0; i < allEles.length; i++){
var element = allEles[i];
element.onchange();
if (isCreatEvent) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
element.dispatchEvent(evt);
}
else
element.fireEvent("onchange");
}
In my firefox addon I have a <listbox>. I want to be able to work a javascript function when I left-click on an item in the box. The function should retrieve the item's textual value.
Now, my function does get called when I click on a listitem, as I've placed this in my event listener's onLoad call:
var myListBox = document.getElementById("myListBoxID");
myListBox.addEventListener("click", function(event){
var target = event.target;
while (target && target.localName != "listitem"){
target = target.parentNode;
}
if (!target){
return; // Event target isn't a list item
}
alert(target); //returns blank
alert(target.id); //returns blank
alert(target.getAttribute("value")); //returns blank
alert(target.getAttribute("text")); //returns blank
alert(target.getAttribute("id")); //returns blank
var targetid = document.getElementById(target.id);
alert(targetid); //returns null
}, false);
},
The xul goes something like this:
<listbox id="listbox1">
<listcols /><listcol flex="1"/><listcol flex="1"/></listcols>
<listitem><listcell class="column1" label="label1" value="value1"</listcell><listcell label="cell1"></listcell></listitem>
<listitem><listcell class="column2" label="label2" value="value2"</listcell></listitem><listcell label="cell2"></listcell>
</listbox>
However, I can't get it to display the text of the items. As you can see above, I don't seem to have a proper handle on the target
I've gotten the original code from here, and got the EventListener working here.
How can I get the value of the listcells? I've tried everything!
You are using this code:
while (target && target.localName != "listitem"){
target = target.parentNode;
}
It will go up from the actual click target looking for a <listitem> tag. Yet the text isn't stored in the <listitem> tag, it's in the <listcell> - so you simply should be looking for that one in the hierarchy:
while (target && target.localName != "listcell"){
target = target.parentNode;
}
alert(target.getAttribute("value"));
You can't detect a click on just a listcell. The closest you can go is to detect a click on a listitem. After that, you have to drill down using code.
So, use .childNode on your target. Since you have only two listcells in your listitem, if you are trying to capture the second cell, use childNodes[1]. `childNodes[0] will refer to the first cell.
onLoad: function(){
var mylistbox= document.getElementById("mylistboxID");
mylistbox.addEventListener("click", function(event){
var target = event.target.childNodes[1];
if (!target){
return; // In case there is not target
}
alert(target.getAttribute("label") + target.getAttribute("label"));
}, false);
},