On IE11 Windows 8.1, button remains highlighted after clicking - javascript

I'm working on a (relatively) complex web application, and encountering a very specific issue. On the specific OS and Browser mentioned in the title, in one of the forms that shows up, when some buttons are clicked, they remain "highlighted" as if the mouse were still hovering over them, even when the mouse leaves the button element or if the user clicks on a separate element on the page. It looks like this:
(the "First Name", "Last Name", etc. elements are the buttons)
The specific behavior of these buttons is that when they are clicked, they put a specific bit of text into the text form shown, then focus the text form. This is done using some standard jQuery functions (.val(), .focus(), etc.) On any OS/browser pair I have seen other than Win8.1/IE11, this does not happen. (it doesn't happen on Win7/IE11 even!) And as far as I can tell, there are no other handlers attached to the buttons than click, and the only styles that seem to be applied have to do with size, color, and text.
Honestly, I'm completely stumped about this behavior. I've even tried doing things as making sure to call .blur() on the button, trigger mouseout events, etc. and nothing seems to make a dent in this. The only thing that seems to cause the highlighting to go away is to click very close to the buttons, but not on them, or to drag-select some text.
EDIT December 7, 2015: This has been on my backburner for a while with other more-pressing issues to deal with. But I'm going to do my best to provide a nice update with more information so that this can hopefully be resolved. (assuming this isn't some browser bug in the specific version of IE on Windows 8.1)
I have verified that the only event handler attached to the button is an onclick handler defined in the HTML itself by checking what IE11's dev tools see as the event handlers on the element:
<input type="button" value="${someValue}" onclick="autoText('${token}');"/>
autotext is a very simple function that does what is described above, but here is the actual source code for it:
function autoText(token) {
if(null != lastFocus) {
var str = lastFocus.val(),
tokenStr = '$\{' + token + '\}',
newStr = str.substring(0, pos) +
tokenStr + str.substring(pos, str.length);
lastFocus.val(newStr);
var lastPos = pos + tokenStr.length;
lastFocus.focus();
lastFocus.caret(lastPos);
lastFocus.keypress();
}
where lastFocus is set to the text input element that was last focused and pos is set to where the caret is in the text input element.
The button that has the onclick listener is in the following hierarchy, as given to me by Chrome: html -> body -> div -> div#templateFormDiv -> form#prefsEmailTemplatesSave -> table -> tbody -> tr -> td -> input (where -> indicates it is the parent)
html has the classes mod-js mod-canvas mod-no-touch mod-postmessage mod-contenteditable mod-cookies mod-filereader mod-formvalidation mod-fileinput mod-formattribute mod-getusermedia
body has no CSS classes
div has ui-dialog ui-widget ui-widget-content ui-corner-all ui-front ui-dialog-buttons ui-draggable ui-resizable
div#templateFormDiv has the classes hidden preferencesDialog ui-dialog-content ui-widget-content
form#prefsEmailTemplatesSave has the class savePrefsForm
table, tbody, tr, td, and input don't have any classes associated with them.
The CSS rules for all the listed classes, ids, and tags are as follows:
*{padding: 0; margin: 0;}
body {
margin:0;
padding:0;
height:100%;
font-family:'Lucida Grande',Verdana,Arial,Sans-Serif;
min-height: 100%;
font-size: 62.5%;
color: #333;
}
form {
margin:0;
padding:0;
}
.hidden {
display: none;
}
All other classes, as far as I can tell, are included as part of bits of jQuery or are there as markers for DOM queries. (e.g.: savePrefsForm)
div#templateFormDiv has a dialog attached to it via a jQuery dialog() call.
form.savePrefsForm is selected and all found elements have an ajaxForm attached to them via jQuery. In a similar vein, #prefsEmailTemplatesSave is selected and submitted if the #templateFormDiv dialog has its save button pressed and it passes some validation logic.
The last thing I can think of to note here is that there is a TinyMCE editable text area next to where the buttons are. No parent/child relationship, but rather a sibling one.
I'll try to think of any further details that matter and include them if I do, otherwise I'll just answer any further questions that anyone has about what is going on.

Have same issue on IE 9-11 Windows 8.1-10. Every button that not cause postback, remains highlighted after click. On the other sites buttons work properly, so it was clearly my issue. I found it because CSS selector "hover" in my styles:
.navigationLink:hover {
}
I have changed it to:
a.navigationLink:hover {
}
and now buttons highlighting works properly. I can't understand what kind of magic is it. Any help would be appreciated.

Related

Workaround for :empty Selector Not Being Updated When Children Added in IE11

I have simple css statement I want to apply to some HTML that basically only shows an element when its previous sibling is not empty. The use case is to only show a "Clear" button when a list has items in it.
<ul></ul>
<button>Clear</button>
ul:empty + button
{
display: none;
}
The problem is that whenever my javascript inserts a new list item into the list, Chrome appears to work correctly and automatically make the button visible. IE11 however fails to show the Clear button. This JS fiddle illustrates the problem: http://jsfiddle.net/xw4nbnz3/
What is the easiest workaround for this problem to make ie11 work? Preferably staying in CSS only.
Pretty sure I'd seen this before, but I can't find the previous question anymore so I'll take a stab at it.
This looks like a typical repaint bug: IE will understand your CSS just fine... if the list starts out with items, and if you write a function to empty the list instead, it will not update the button either. Unfortunately, repaint bugs aren't known for having pure CSS workarounds.
Fortunately, they don't usually need them. Since this only happens when you change the DOM, you can easily work around this within the JS, and it doesn't take very many lines. It looks like removing the list itself first, before inserting the new item, then putting the list back in resolves this issue whilst not creating any problems in other browsers:
function AddListItem()
{
var mylist = document.getElementById("mylist");
if (!mylist)
return;
var parent = mylist.parentElement;
var button = mylist.nextElementSibling;
parent.removeChild(mylist);
var li = document.createElement("li");
li.appendChild(document.createTextNode("Hello, world!"));
mylist.appendChild(li);
parent.insertBefore(mylist, button);
}
Tested on IE11 on Windows 7, Windows 8.1, and Windows 10 RTM (build 10240), and Microsoft Edge on Windows 10 RTM. Note that removing the list after inserting the item (right before putting it back in) does not work; you will need to remove it first.
The issue is related to cascaded selectors like the button in the selector ul:empty + button.
It's safe to use the :empty pseudo-class in IE11 when it applies to DOM element itself https://codepen.io/egeneralov/pen/geRQXz.

EventListener cloning in userscript

I have this function:
function main() {
var bottomArea = document.getElementsByClassName("bottom-area");
for (var i = 0; i < bottomArea.length; i++) {
var showDialogLink = document.createElement("a");
showDialogLink.innerHTML = "link";
showDialogLink.onclick = function(){showSelect(this);return false;};
bottomArea[i].insertBefore(showDialogLink, bottomArea[i].childNodes[3]);
}
}
So far the code works just fine. When I click the newly created link, it calls showSelect(this) function just fine.
The problem is there is another userscript/browser extension (which I don't have access to - it's not mine), which basically clones whole another div in which 'bottom-area' div is nested. This is all right too, but the problem is that it doesn't clone my function trigger and those newly cloned instances (I'm not really sure what is their nature) of that link do no longer trigger showSelect(this) function. Only the first one created by my userscript does.
Is there some way in which I should add my function trigger on my link, that will stay even after cloning/copying?
EDIT: I'll just edit to show html tree:
This is at the beginning:
<div>
<div class="bottom-area"></div>
</div>
My userscript adds a link with an onclick eventlistener on the 'a' tag:
<div>
<div class="bottom-area"><a>link</a></div>
</div>
The other userscript basically clones it (there is a textarea inside the div and its value gets cloned too), but without the eventlistener, so clicking on the cloned links no longer triggers my function.
EDIT2: If it helps, the userscript I'm creating is a reddit userscript. I'm adding small functionality to commenting and adding a link right next to the 'reddiquette' link under the comment text field. That works with the pre-generated text field. However when I click 'reply' down the comment tree, the whole div together with text field, submit button and my link gets cloned under the comment I'm replying too, but my link no longer has the function trigger on itself.
The easiest solution may be to simply use HTML event attributes (instead of addEventListener), such as <a onclick="dostuff();">link</a>, because the attribute should be preserved during 'cloning'. See this fiddle (tested in Firefox 40) for an example.
Letting the code in the onclick attribute interact with your userscript may be be a little difficult because it runs in a different JavaScript environment. Luckily there's plenty of possible workarounds, depending on your exact needs.

CKEditor, custom object with children

I am trying to create a plugin for CKEditor that adds a custom object with children.
Example:
<div>
<img src="someimage.jpg" />
<p>
Some text
<span>Some subtext</span>
</p>
<img src="someStaticImage.jpg" />
</div>
In the onOk function i have:
---snip---
this.imgElement.setAttribute('src',path + data.imageSrc);
this.staticImgElement.setAttribute('src',path + 'images/staticimg.jpg');
this.imgElement.appendTo(this.element);
this.imgElement.appendTo(this.element);
this.staticImgElement.appendTo(this.element);
---snip---
I would like for this block to behave as a single element, meaning that pressing backspace deletes the whole block, double clicking on it opens the edit dialog...
Any idea how i could do that?
I came close with setting
this.element.setAttribute('contenteditable','false');
However this doesn't allow content to be inserted before "it", if "it" was the first element in the ckedit window.
EDIT:
More info:
I'm using CKEditor 4.0, inline version
I wish for my "object" to be like the "image" plugin, where when you double click on the image, a dialog opens, the same one as when you create the object (where you set the src, width...).
I managed to make it similar to it, but because it is a div with child elements, CKEditor treats each part as seperate, which makes the deleting of the object (with backspace) behave in a wierd way, only parts of it get deleted, backspace needs to be pressed multiple times to delete the entire object.
I'm a CKEditor core developer and I think that I've got an interesting news for you :). Coincidentally, right now we're working on Widgets feature which is exactly what you mean.
Making some fragments of the page non-editable by setting contenteditable=false make them unusable. Selecting, copying&pasting, right clicking, using arrow keys - all this is at least partially broken. And it's even worse if you try to add a nested editable element (editable inside non-editable), because e.g. it can be deleted from inside.
That's why we decided to implement a nice API for creating widgets and fix all these bugs under the hood. Maybe not all bugs and in all browsers at the beginning, because there's huge (really... I mean huuuuge :P) amount of them and of course no standard behaviour between browsers at all. But it will be a good start. First version of widgets that will be released in upcoming CKEditor 4.2 should be usable - this is our goal. Then we'll focus on stabilizing the implementation.
PS. CKEditor Roadmap says that CKE 4.2 will be ready in 11 days and this is not true, unfortunately. We're delayed, but I don't want to estimate now how much.
You indicated that you've created the plugin that handles the object, but that the problem you want to solve is the inability to insert content before the object when it's the first item.
It looks like this bug report is about this issue:
Can't move cursor behind non-editable element (or placeholder) in CKEditor
I used my plugin to insert this block of code into the editor:
<div contenteditable="false">
<img src="someimage.jpg" />
<p>
Some text
<span>Some subtext</span>
</p>
<img src="someStaticImage.jpg" />
</div>
You can also add the display: inline-block; style if you want (see discussion below):
From my tests, it seems like you can't put content before the object using the back arrow, but if you back arrow to that row of content and press the home key, you can type before the object. It also seems that if you click with your mouse in the upper left corner, you can add content before the object.
Both approaches push the object onto the next line because it's a <div>, you can change the style of the div to display: inline-block; if you want the object to stay on the first row. I tried just making the object <div> a <span> instead, but then it becomes possible to edit parts of the object.
You can't use backspace to delete the object, but you can click the object to select it and then delete it.
I checked the info discussed above with Firefox 20 and IE 9 on Win 7. Google Chrome has a bunch of problems:
When the block of HTML is inserted with the plugin, the contenteditable="false" attribute is stripped out.
So I tried to see how it worked if I just pasted that block of code into CkEditor while in source mode. The contenteditable="false" attribute wasn't stripped out, but the whole content area became uneditable.
My tests were using CkEditor 3.6.1, so this may not be a problem in CkEditor 4.X.
This bug report seems to be about the problem I encountered with being unable to do anything in the content area using Chrome, the report indicates version 3.X:
ContentEditable, Image and Chrome
Additional Info
Here's a plugin from CKSource that might be helpful:
Magic Line
The description:
With this plugin you can create new paragraphs into spaces where normally would be impossible to reach. For example, above a table at the beginning of the document.
Here's my plugin that inserts content into the editor, it doesn't solve the problem you have, but you might use it to add functionality to your plugin. I'll write the full instructions in case someone who hasn't created a plugin finds this and wants to give it a try.
Here's the code that goes in the ckeditor/plugins/cwmarinsertsnippet/plugin.js file:
/**
* Plugin to insert the contents of an element into the editor content area.
*/
// Register the plugin with the editor. cwmarinsertsnippet
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.plugins.html
CKEDITOR.plugins.add( 'cwmarinsertsnippet',
{
// The plugin initialization logic goes inside this method.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.pluginDefinition.html#init
init: function( editor )
{
// Define an editor command that grabs the content of an element and inserts it into the content area.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.editor.html#addCommand
editor.addCommand( 'cwMarInsertSnippet',
{
// Define a function that will be fired when the command is executed.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.commandDefinition.html#exec
exec : function( editor )
{
// Create an element based on a native DOM element.
var codesnippet = new CKEDITOR.dom.element( document.getElementById( 'resmar' ) );
//alert( codesnippet.getHtml() );
// Insert the element content into the document.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.editor.html#insertHtml
editor.insertHtml( codesnippet.getHtml() );
}
});
// Create a toolbar button that executes the plugin command.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.ui.html#addButton
editor.ui.addButton( 'CwMarInsertSnippet',
{
// Toolbar button tooltip.
label: 'Insert Search Box',
// Reference to the plugin command name.
command: 'cwMarInsertSnippet',
// Button's icon file path.
icon: this.path + 'images/buttonicon.gif'
});
}
});
It needs to be added to the config file with:
config.extraPlugins = 'cwmarinsertsnippet';
and to make the button visible, the button name "CwMarInsertSnippet" needs to be added to the config toolbar entry:
CKEDITOR.config.toolbar_XXXX
[
Snip...
{ name: 'tools', items : [ 'About','CwMarInsertSnippet' ] },
... End Snip
];
The button for the plugin should be 13px X 13px (for CkEditor 3.X, not sure about version 4.X). It gets placed here:
ckeditor/plugins/cwmarinsertsnippet/images
The name should match the one used in the editor.ui.addButton function at the end of the plugin code (this path can really be anywhere you want).
Here's an example of the code to be added to the page where CkEditor is being used:
<div id ="resmar">
<div contenteditable="false">
<img src="someimage.jpg" />
<p>
Some text
<span>Some subtext</span>
</p>
<img src="someStaticImage.jpg" />
</div>
</div>
You can also add the display: inline-block; style if you want:
<div style="display:inline-block" contenteditable="false">
The container element can be hidden with styling if it shouldn't appear on the page.
Basically, just put the content you want to insert inside the element with the target ID
<div id ="resmar">
Content to be inserted.
</div>
Of course the plugin name and the element ID can be anything you like.

Prevent Chrome from wrapping contents of joined <p> with a <span>

I have observed an undesirable behaviour in Chrome that occurs when one joins two <p>'s by deleting the separation between them. Although the <p> tags are joined properly, Chrome wraps the right-most <p> tag's content with a <span>.
Edit: this happens for all block elements, not just p tags.
Example:
For example, when the separating </p><p> are deleted from the following block:
<div contenteditable="true"><p>p one.</p><p>p two.</p></div>
It becomes:
<div contenteditable="true"><p>p one.<span style="font-size: 16px; line-height: 1.44;">p two.</span></p>
Example in a fiddle: Chrome wrapping contents of joined <p> with a <span>.
Question:
Is there an easy way to prevent chrome from doing this? It results in horrible markup that I'd like very much to be rid of.
There is a way but you need to pro-actively set a few styles. The idea is to tell Chrome that the styles are already taken care of, so it doesn't need to add SPAN to meet the styles requirement.
basically, you need to add the chrome added styles to a span class under your contenteditable div ( see example below).
Edited fiddle
For you example:
I added an "edit" class to the contenteditable DIV
I added an .edit p, span class in the style
This becomes:
.edit {
border: 1px solid gray;
padding: 10px;
}
.edit p, span {
line-height: 1.44; font-size: 16px;
}
And the DIV:
<div contenteditable="true" class="edit">...</div>
Note that you normally don't need the font-size: 16px;. I needed to add this one because fiddle defines some font size in contenteditable. On a standalone page I didn't need it.
You need to apply this Chrome 'patch' to any elements where it happens (so if you need UL, OL... then add what is needed following my example logic above)
I know it is not really an answer to solve it, but a hint how it could be fixed (but it is to long to be a comment to Petah question how i would solve it)
in general you would check when such bugs could happen. for the case of the span creation you would listen to all keydown and keypress events and check if the key is the backspace/delete key or for every key that inserts chars if it is a real selection.
if this is the case then you need to check the current selection (either the position of the insert mark, or the real selection) then you know which is the next following text-element or node. then you need to check the in the next following keypress and keyup if there is a span created directly after your insert mark. depending on the browser bug you need some further checking. if there is one create unwrap its content again. additionale Mutation events and helper attributes could be used.
But i need to say that i gave up in doing this myself and switched over to ckeditor 4. most of the it's features i don't need and it is a really big library. but cause of the huge number of such bugs i did not see another solution for me.
EDIT
Here an update of the js fiddle that shows the idea with a Mutable event:
http://jsfiddle.net/THPmr/6/
But that is not bullet proofed, it is just to show how it could be achived ( only tested in chrome 27.0.1422.0, and probably would not work if more then one text element is contained in the second p )
Here is my take on removing the extra spans
document.querySelector('[contenteditable=true]')
.addEventListener('DOMNodeInserted', function(event) {
if (event.target.tagName == 'SPAN') {
event.target.outerHTML = event.target.innerHTML;
}
});
The CSS is influencing how the markup is made inside contenteditable:
div, pre {
border: 1px solid gray;
padding: 10px;
line-height: 1.44;
}
Delete the line-height line and the problem doesn't occur any more.
There are in general several bugs with contenteditable related to default styling : How to avoid WebKit contentEditable copy-paste resulting in unwanted CSS?
EDIT JsFiddle IS indirectly influencing this (tinkerbin behaves differently) because of its' CSS (normalize.css). Try this:
Run your fiddle
Inspect a <p>
Disable all font-size declarations in the CSS stack - including your line-height
do the backspace
there is no inline span
Solution 1 : Use classes and id's.
Don't declare font-size for p or div but for p.main-content, or more simply, .main-content.
If the font-size of your elements inside contenteditable is coming from the browsers' internal default CSS then Chrome won't add extra markup/inline styling.
Solution 2 : Use a Sanitizer.
I'd personally go with #1 as it's never a good practice to specify font-sizes and typo in so generic tags.
The best way I found so far is to listen to DOMNodeInserted and check the tagName. If it is a span, you can remove the tag and but leave the contents. This also keeps the cursor at the correct place.
function unwrap(el, target) {
if ( !target ) {
target = el.parentNode;
}
while (el.firstChild) {
target.appendChild(el.firstChild);
}
el.parentNode.removeChild(el);
}
var AutoFix = true;
document.getElementById('editable')
.addEventListener('DOMNodeInserted', function(ev) {
if ( !AutoFix ) {
return;
}
if ( ev.target.tagName=='SPAN' ) {
unwrap(ev.target);
}
});
I've added a boolean 'AutoFix' so you can disable the automatic dom changes when you do need to insert a span, since this event fires on any dom change. E.g. if you have a toolbar that allows the user to insert something like <span class="highlight">...</span>.
The code has no side effects in IE or FireFox as far as I can see.
This irritated me as well, but I found a solution that works well enough for now.
$('#container span').filter(function() {
$(this).removeAttr("style");
return $(this).html().trim().length == 0;
}).remove();
I simply remove the style tag from the span element and remove it altogether if it's empty. You could probably filter based on the attribute style, but as I'm already doing a loop to check to remove empty spans, I thought it was best to do both at the same time.
It does create a flicker for a microsecond when chrome first tries to insert the style inherited for the span, but you only see that once immediately after deletion.
It isn't perfect, but it's quick and concise.
Found this link from a comment posted on someone's blog:
(Fixed bug where the latest WebKit versions would produce span element…)
https://github.com/tinymce/tinymce/commit/8e6422aefa9b6cc526a218559eaf036f1d2868cf
Please see the answers here: https://stackoverflow.com/a/24494280/2615633
To fix the problem you may just use this plugin: jquery.chromeinsertfix
After several attempts of using provided solutions, I came up with this script:
var content = obj.textContent;
obj.innerHTML = '';
obj.textContent = content;
When someone pastes a text with html encoded chars, putting it to innerHTML results with a valid html tags, that is why I decided to purge innerHTML content before placing content into obj

HTML elements for JavaScript hooks

Most of my development life, I have done a JavaScript generated button like so:
<span>Open/hide</span>
With CSS like
span {
cursor: pointer;
}
This seemed to work for me, and IMO, it looks and seems better than
open/hide
However, I'm starting to think people might use links because they are better accessible to screen readers. It also makes sense that the browser will allow you to tab to a link - but not necessarily a span with a CSS style (like my previous example).
Is there a way to get the best of both worlds? That is, a button that can be tabbed to, accessible to screen readers and also not implying a semantic link to another document, and not having to use hacky ways to stop the link from going anywhere?
Note: These are for JS inserted buttons only, not existing links of which their behavior is changed with JS.
That is, a button that can be tabbed to, accessible to screen readers and also not implying a semantic link to another document
Yes, and curiously enough it's called just that: <button>. Or <input type="button"> if you only need text content. You can then add a load of CSS to it to stop it looking like a default form button if you prefer.
The one problem is IE, which adds an extra couple of pixels of unremovable padding to button widgets, and moves the content of the button down and to the right one pixel when clicked. Often this doesn't matter. If you don't need zero padding you could also compensate by adding padding on all browsers except IE.
Another semantically sound possibility is to use an <input type="image">, although that obviously requires the content to be an image. Relying on the alt text instead doesn't work due to IE's rendering of broken image icons.
Whilst buttons are semantically by far the most appropriate element, if you don't want this styling trouble you can continue to use a span or other meaningless element (like SO does), but at least improve the keyboard accessibility by giving it a tabindex value. It will then be focusable like a link, though unfortunately you need extra scripting if you want to make it activatable from keyboard (catch at least keypress codes 13 and 32). You can also label such an element as a button with ARIA for accessibility, athough to be honest I'm not convinced this really does anything much useful for today's toolset.
Sometimes when the action is related to another element on the page, such as with the suggested ‘open/hide’ button, it seems legitimate to me to use an internal link to that element. (With a JS-assigned onclick though, not any of that inline JS or horrific javascript: pseudo-URLs.)
.hidden { display: none; }
open/hide
<div id="foo"> blah </div>
for (var i= document.links.length; i-->0;)
if (document.links[i].className==='toggler')
document.links[i].onclick= toggle;
function toggle() {
var el= document.getElementById(this.hash.substring(1));
el.className= el.className==='hidden'? 'hidden' : '';
}
or similar idiom in your library of choice. This will do something useful in non-JavaScript browsers; you can also add a rule to check the current location's hash, so that if someone opens your show-link in a new tab that document will be showing the element in question.
It seems doing
<span tabIndex="n">foo</span>
allows you to tab it. However, pressing enter while it's focused doesn't simulate clicking it. Which I guess is easily remedied by putting a keydown event on it as well.
I would say that the button fits your requirements ...
<input type="button" class="js_button" value="Javascript Button" />
CSS:
.js_button, .js_button:active, js_button:focus {
background-color: #FFF;
border: none;
margin: 0 0;
padding: 0 0;
}
Buttons can be tabbed to, are semantic, automatically have tab indexes and keypress actions, and do not imply links to other documents. The only difficulty is removing the mousedown animation -- I'll look around and see if there's anything out there for removing that animation without hacks.
You can link to any object in the page if you provide the id or the name of the element.
For example:
https://stackoverflow.com/questions/1659137/html-elements-for-javascript-hooks#comments-1659137
HTML elements for JavaScript hooks

Categories

Resources