Issue in Chrome, removing CSS with jQuery stays in chrome - javascript

I'm currently working on a table application heavily coded in javascript using jQuery.
When you click on a td cell jQuery pushes 2px solid black to the border property. Then on blur I remove the style attribute with removeAttr to make it revert back to the stylesheet settings. Works fine in IE9, but when I test it in Chrome, the left border resizes, but stays black.
The only thing that seems to get rid of this is opening the console. When I blur with the console open the style tag removes so I don't understand why it's still rendering a black border on the left. Any ideas?
EDIT: I've made a video showing the problem I'm experiencing.
http://www.youtube.com/watch?v=JCmYNOO5u4I
Here's the code:
$("td.display").live('mouseenter', function () {
$(this).addClass('selected');
}).live('mouseleave', function () {
$(this).removeClass('selected');
});
The CSS is:
table TD.selected {
border: 2px solid;
border-color: Black;
}

Rather than using $.removeAttr, use $.css to restore the CSS. $.removeAttr isn't going to work properly due to how CSS persists, most likely.
What you should really do is add a class on focus, then remove that class on blur. That is, $.addClass and $.removeClass, documented at the respective links.

Instead of using JavaScript to achieve this trivial effect, use CSS. The :hover pseudo-selector is well-supported in all modern browsers.
td.display:hover {
border: 2px solid black;
}
I have adjusted your selectors, since <td> elements are always contained in a <table> element.
Demo: http://jsfiddle.net/JKM7e/
Update
This is one of the many table-css-related bugs in Chrome. A work-around is to initiate a re-render. To solve the issue, let Chrome render the page again. This fix should not activate in non-webkit browsers, so an additional variable is added.
I've also optimized your code, and replaced the deprecated live with on. The new revision can be found here.
// true if webkit, false otherwise
var isWebkit = !!$('<div style="-webkit-border-radius:1px">')[0].style.cssText.length;
$(document).on('blur', 'input.editCell', function () {
var $thisParent = $(this).parent(); // <-- Optimizing by caching
// Remove Highlight and bold around cell
$thisParent.removeClass('selected');
var colRow = $thisParent.attr('id').split('_');
$('th:containsExactly(' + colRow[0] + ')').removeAttr('style');
$('td.yAxis:containsExactly(' + colRow[1] + ')').removeAttr('style');
// Change class back to display
$thisParent.attr('class', 'display');
// Take the entered value and put it in the cell
$thisParent.html(this.value);
// if(isFunction(this.value) === 'SUM')
if (isWebkit) { // Fix for Webkit bug: render the table again
// Without jQuery, for efficiency
var style = document.getElementById('spreadsheetTable').style;
style.color = '#111'; //<--1. Change style
setTimeout(function(){style.color='';}, 4); //<--2. Fix
}
});

Two options:
- instead of removeAttr, reset the style with .css()
- use a class instead, and use toggleClass() in the focus and our events
The second option is definitely preferable.

Related

How to force a drop-down to move down on IE 11?

I have the following drop-down :
<select>
<option></option>
<option>Closed</option>
<option>Open</option>
</select>
with the associated style:
select {
font-family: Cursive;
width:200px;
position: relative;
z-index: 100;
padding-right: 25px;
}
My problem is that the drop-down is moving upward on IE 11:
Where as on chrome it is working fine.
Any idea ?
Like mentioned in the comments, select menus are very browser specific and hard to style. Some even send the options into the twilight zone where they are seemingly not even a part of the window and any events will return null. It might not be worth trying to get this to look the same across browsers, also because of the mobile implementations, but I happened to be making something like this for no apparent reason. As it coincides with your question I might as well post it.
It's not the prettiest thing when it comes to HTML and CSS because it requires four additional elements - one wrapper (commonly used for styling select boxes with overflow hidden but I took a slightly different approach because I thought it looked better) and three absolutely placed elements. One is a styled button, another will hide the scrollbar that appears and the third is a minor hack.
Most important thing is that the user will not be able to click the select menu itself. When this happens, most is lost because after that it's limbo. For that the third element will be used. It will be put on top of the select box. Then when it's clicked, instead of directly opening the menu it will be faked by changing the size of the select element. The div covering the right side also serves another purpose. It's initially placed at the bottom and by getting it's offset we'll know the height of the box. This will be used to resize the button and set the correct size for the overlaying div.
Looks to be behaving quite predicatbly on all major Windows desktop browsers. For the mobile implications this script uses a touch support feature test and reverts to normal layout if that is the case. Could probably be tweaked (with a screen size check) to not exclude trackpad users.
Demo
Not too much relevant CSS. But important to style body background the same as the bar on the right. Transparency is used so the actual menu isn't visible before the image for the button loads.
$(function() {
var hub = $('#box'), list = $('select'),
impel = $('#open'), commit = $('#mark'), tract = $('#refer'),
zenith = tract.position().top,
extent = list.children().length, active;
if (touch()) {
impel.add(commit).add(tract).remove();
hub.fadeTo(0,1);
return;
}
impel.add(commit).height(zenith);
tract.addClass('bar');
hub.fadeTo(0,1).on('mouseup click', function(e) {
e.stopPropagation();
});
commit.mouseup(function() {
flip();
show();
active = true;
});
list.add(impel).click(function() {
flip();
active = !active;
if (active) show();
else hide();
});
$(window).click(function() {
if (active) {
flip();
hide();
active = false;
}
});
function show() {list.attr('size', extent)}
function hide() {list.removeAttr('size')}
function flip() {commit.toggle()}
function touch() {
return 'ontouchstart' in window
|| navigator.maxTouchPoints > 0
|| navigator.msMaxTouchPoints > 0;
}
});

Successive background-color animation request failing with jQuery 1.6.1

I'm trying to "flash" an input box by changing the background color briefly and then reverting back to the original bg color using jquery to indicate an error and grab the users attention.
Here's a fiddle to demonstrate what I'm trying to do.
I have to use jquery version 1.6.1. In the fiddle demo, it's using 1.6.4 and the color of the input box never changes at all. Actually, it doesn't work even with 1.11. In my local tests with my code, the input box changes red with the first animation call, but fails to do anything for the second animation call (to revert the bg color back to white). It just stays red.
I'm using very similar code to do the same thing in another site, except using jquery 1.11 and it works fine.
Is this just a compatibility issue? Is there some way I can make this work properly with version 1.6.1 ?
Here's the code:
function flashInputBox(id) {
var input = $('#'+id);
input.focus();
input.stop(true).animate({'background-color': '#EC8686'}, 350, function() {
input.stop(true).animate({'background-color': '#FFFFFF'}, 1000);
});
}
I forgot to mention that I'm using jQuery UI v1.8.18
The problem is properly replicated now in this fiddle (same code, just added jQuery UI 1.8.18).
Do you need to use jQuery? If not, this is way easier in CSS using key frames. If it is, skip my CSS explanation.
CSS
This still uses jQuery, but it gives the animation job to CSS, making your code more legible. I set this up in jsFiddle if you want to check it out: jsFiddle Example
First, setup a keyframe:
#keyframes pulse{
from {
background: #ec8686;
}
to {
background: #ffffff;
}
}
#-webkit-keyframes pulse{
from {
background: #ffffff;
}
to {
background: #ec8686;
}
}
and attach it to your existing input:
#my-input{
...
-webkit-animation: pulse 5s infinite;
-webkit-animation-play-state: paused;
...
}
Then the jQuery becomes a matter of letting the animation play for a few seconds:
function doIt() {
$("#my-input").css("-webkit-animation-play-state", "running");
setTimeout(function() {
$("#my-input").css("-webkit-animation-play-state", "paused");
}, 5000);
}
Also, you don't even need the jQuery to trigger the animation. The button click can directly trigger a CSS animation, however I figured you have some sort of code to check what's in the box for accuracy, so that why I kept your old function.
Note that this keyframe ends suddenly, so you can totally have a 0%, 50%, 100% keyframe instead.
Now for the raw jQuery way:
jQuery
For your jQuery, its much easier just to either specify your input directly (aka $("#my-input-name")), or if its just one input, I got it working just by using the following code instead:
function doIt() {
...
input.stop().animate({'background-color': '#EC8686'}, 350, function() {
// just say input here //
input.animate({'background-color': '#FFFFFF'}, 1000);
});
}
Colors aren't numeric values, so they can't be animated. From the jQuery documentation for .animate, emphasis mine:
All animated properties should be animated to a single numeric value, except as noted below; most properties that are non-numeric cannot be animated using basic jQuery functionality (For example, width, height, or left can be animated but background-color cannot be, unless the jQuery.Color() plugin is used). Property values are treated as a number of pixels unless otherwise specified. The units em and % can be specified where applicable.
If you don't want to (or can't) use the jQuery.Color plugin, you'll need to animate the color "manually", e.g. by setting an interval and changing the color at each step.

Mouseover Highlighting of Page Elements

I am looking for either an open source solution already available or for someone to point me in the right direction to find this. I am creating a Firefox extension that works for elements from the DOM. In Firefox and Chrome, there are element inspectors that highlight the region and/or element that your mouse is currently hovering over, such as the div it is currently hovered over or a button if it is hovered over that. I'm looking for how to implement that functionality into my own extension. Let me know if there are any solutions to this, thanks!
try something like this:
var lastBoxedEl;
function moused(e) {
var target = e.target; //experiment, try e.currentTarget, e.originanalTarget
if (lastBoxedEl) {
lastBoxedEl.style.outline = 'none'
}
lastBoxedEl = target;
target.style.outline = '5px solid red';
}
document.body.addEventListener('mouseover', moused, false);
I did something in the past for a demo. Here is the source opened for your request:
https://github.com/kashiif/hilight-dom-element-on-hover
Note that this is not complete and may require finishing e.g.:
The red box is left behind after the element is clicked.
There is a little re-flow of element because a border is being added. You may modify the box class such that box-sizing is set to border-box
If you like you may send me a pull request of the changes.

Get the background of the row in a table to change for a click?

How can I get the background of the row in a table to change colors when a proper click is performed by the user?
I tried this via the :active pseudoclass, but doesn't work as I want. For example, on a mobile device touch screen, as soon as the user touches the screen, the intersected row will change colors, even though it's not a click [a click being a short mouseDown+mouseUp combination].
Here's the table:
<table>
<tbody>
<tr>
<td align="left" style="vertical-align: top;">
<div class="GPBYFDEEB"
__gwtcellbasedwidgetimpldispatchingfocus="true"
__gwtcellbasedwidgetimpldispatchingblur="true">
<div>
<div class="GPBYFDEAB" tabindex="0" style="outline:none;" __idx="0" onclick="">
<div class="GPBYFDECB" style="outline:none;" __idx="1" onclick="">
<!-- finally this is me. -->
<div class="tableRow">
Here's my css:
.tableRow {
background-color: green;
}
.tableRow:active {
background-color: red;
}
Is there a way to do this? (I'm using gwt to generate the above html, but don't think it matters here).
Just to be precise, what'd I'd like is for the background color of the row to change to red after an onclick event is hit. After a brief period, revert the background to its original color. I'm basically trying to reproduce the visual effect of clicking a list item on iOS or android native widgets.
Thank you
JavaScript makes it pretty straight-forward:
var rows = document.getElementsByClassName("tableRow"); //get the rows
var n = rows.length; //get no. of rows
for(var i = 0; i < n; i ++) {
var cur = rows[i]; //get the current row;
cur.onmousedown = function() { //when this row is clicked
this.style.backgroundColor = "red"; //make its background red
};
cur.onmouseup = function() {
this.style.backgroundColor = "green";
}
}
If you have jQuery included, it's even simpler:
$(".tableRow").mousedown(function() {
$(this).css("background-color", "red");
});
$(".tableRow").mouseup(function() {
$(this).css("background-color", "green");
});
A little demo that uses the pure JavaScript version: little link. (I took the liberty to change the colors a bit!).
This is how you do it in GWT: you attach a handler to your CellTable or DataGrid.
myTable.addCellPreviewHandler(new Handler<myObject>() {
#Override
public void onCellPreview(CellPreviewEvent<myObject> event) {
if ("click".equals(event.getNativeEvent().getType())) {
myTable.getRowElement(event.getIndex()).getStyle().setBackgroundColor("red");
}
});
}
If you don't like the way it looks (background color being different from border color), you can use this instead:
myTable.getRowElement(event.getIndex()).addClassName("redRow");
This way you can specify more rules in your redRow CSS class, like:
.redRow {
background: red;
border: 2px solid red;
}
When you do not need this color for your row, you just remove this class or use .getStyle().clearBackgroundColor(), if you prefer the former solution.
Changing styles on click is indeed something that requires javascript and that is not veryu difficult, as #Abody97 already demonstrated in his answer.
I think it is worth mentioning what the :active pseudo selector stands for, as you seem to be confused. Personnaly I think its name is pourly chosen, it would be much clearer if it was called :down or something. Cause that is exactly what it is. It targets the down state of a button or link.
Each link or button has 3 states:
- default: no pseudo selector required. You could call this the 'up' state.
- hover: targeted with the :hover pseudo selctor. Only usefull when working with the mouse, and triggered when the mouse is over the element (for modern browsers this is any element, not just a link or button)
- active: this is the down state as I mentionned before. It is targeted by the :active pseudo selector. When using a mouse it is not realy that usefull cause it will only be triggered as long as the mouse button is held down. On a toutchscreen it is much more usefull cause it indicates the user tapped correctly.
I am not sure what the content of your table is going to be (your html snippet stops when it becomes interesting), but I think it is worth mentionning the :focus pseudo selector as well. This one gets triggerd when an element 'has focus'. It works only on input elements (input, textarea) or links (typicly only usefull when navigating with the keyboard). I believe this is the one you where after when you where trying to use the :active.
I set up a small example with the :focus here: http://jsfiddle.net/6tN6B/
Especially the last sample could be usefull for you. It can be done easier with js, but I am a big fan of using js only when it is absolutly nessecary, so non js users get the same experience on your site.

Global override of mouse cursor with JavaScript

In my web application I try to implement some drag and drop functionality. I have a global JavaScript component which does the the basic stuff. This object is also responsible for changing the mouse cursor, depending of the current drag operation (move, copy, link). On my web page there are various HTML elements which define an own cursor style, either inline or via a CSS file.
So, is there a way for my central drag and drop component to change the mouse cursor globally, independent from the style of the element the mouse cursor is over?
I tried:
document.body.style.cursor = "move"
and
document.body.style.cursor = "move !important"
But it doesn't work. Every time I drag over an element which defines a cursor style, the cursor changes to that style.
Sure, I could change the style of the element I'm currently dragging over, but then I have to reset it when I leave the element. This seems a little bit to complicated. I'm looking for a global solution.
Important Update (2021):
The MDN page for element.setCapture() clearly indicates that this feature is deprecated and non-standard, and should not be used in production.
The browser support table at the bottom of that page indicates that it's only supported in Firefox and IE.
Original answer below
Please: don't massacre your CSS!
To implement a drag and drop functionality, you have to use a very important API: element.setCapture(), which does the following :
All mouse events are redirected to the target element of the capture, regardless of where they occured (even outside the browser window)
The cursor will be the cursor of the target element of the capture, regardless where the mouse pointer is.
You have to call element.releaseCapture() or document.releaseCapture() to switch back to normal mode at the end of the operation.
Beware of a naïve implementation of drag and drop: you can have a lot of painful issues, like for example (among others): what happens if the mouse is released outside the browser's window, or over an element which has a handler that stops propagation. Using setCapture() solves all this issues, and the cursor style as well.
You can read this excellent tutorial that explains this in detail if you want to implement the drag and drop yourself.
Maybe you could also use jQuery UI draggable if possible in your context.
I tried using setPointerCapture which worked great. The downside is, that (of cause) all pointer events will not work as before. So I lost hover styles etc.
My solution now is pretty straight forward and for my usecase better suited then the above CSS solutions.
To set the cursor, I add a new stylesheet to head:
const cursorStyle = document.createElement('style');
cursorStyle.innerHTML = '*{cursor: grabbing!important;}';
cursorStyle.id = 'cursor-style';
document.head.appendChild(cursorStyle);
To reset it, I simply remove the stylesheet:
document.getElementById('cursor-style').remove();
document.body.style.cursor = "move"
should work just fine.
However, I recommend to do the global styling via CSS.
define the following:
body{
cursor:move;
}
The problem is, that the defined cursors on the other elements override the body style.
You could do someting like this:
your-element.style.cursor = "inherit"; // (or "default")
to reset it to the inherited style from the body or with CSS:
body *{
cursor:inherit;
}
Note however, that * is normally considered a bad selector-choice.
Unfortunately element.setCapture() does not work for IE
I use a brute force approach - open a transparent div on top of entire page for the duration of drag-drop.
.tbFiller {
position:absolute;
z-index:5000;
left:0;
top:0;
width:100%;
height:100%;
background-color:transparent;
cursor:move;
}
...
function dragStart(event) {
// other code...
document.tbFiller=document.createElement("div");
document.tbFiller.className="tbFiller"
}
function dragStop(event) {
// other code...
document.tbFiller.parentNode.removeChild(document.tbFiller);
}
Thanks to some of the other answers here for clues, this works well:
/* Disables all cursor overrides when body has this class. */
body.inheritCursors * {
cursor: inherit !important;
}
Note: I didn't need to use <html> and document.documentElement; instead, <body> and document.body work just fine:
document.body.classList.add('inheritCursors');
This causes all descendant elements of <body> (since it now has this inheritCursors class) to inherit their cursor from <body> itself, which is whatever you set it to:
document.body.style.cursor = 'progress';
Then to yield back control to the descendant elements, simply remove the class:
document.body.classList.remove('inheritCursors');
and to unset the cursor on the <body> to the default do:
document.body.style.cursor = 'unset';
This is what I do and it works in Firefox, Chrome, Edge and IE as of 2017.
Add this CSS rule to your page:
html.reset-all-cursors *
{
cursor: inherit !important;
}
When the <html> element has the "reset-all-cursors" class, this overrides all cursors that are set for elements individually in their style attribute – without actually manipulating the elements themselves. No need to clean up all over the place.
Then when you want to override your cursor on the entire page with that of any element, e. g. the element being dragged, do this in JavaScript:
element.setCapture && element.setCapture();
$("html").addClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", $(element).css("cursor"), "important");
It uses the setCapture function where it is available. This is currently just Firefox although they say it's a Microsoft API. Then the special class is added to the entire document, which disables all custom cursors. Finally set the cursor you want on the document so it should appear everywhere.
In combination with capturing events, this may even extend the dragging cursor to outside of the page and the browser window. setCapture does this reliably in Firefox. But elsewhere it doesn't work every time, depending on the browser, its UI layout, and the path along which the mouse cursor leaves the window. ;-)
When you're finished, clean up:
element.releaseCapture && element.releaseCapture();
$("html").removeClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", "");
This includes jQuery for addClass and removeClass. In simple scenarios you could just plain compare and set the class attribute of document.documentElement. This will break other libraries like Modernizr though. You can get rid of the css function if you know the element's desired cursor already, or try something like element.style.cursor.
A performance-acceptable, but not ideal either, solution that I ended up using, is actually to change the cursor prop of element directly under the pointer, and then return it back to original when pointer moved to another element. It works comparatively fast, as just a few elements change their style while moving pointer around, but visually you might sometimes see a short glimpse of "original" cursor. I consider it a much more acceptable tradeoff.
So, the solution, in TypeScript:
let prevElement: HTMLElement | undefined;
let prevElementOriginalCursor: string | undefined;
export const setTemporaryCursor = (element: HTMLElement, cursor: string | undefined) => {
// First, process the incoming element ASAP
let elementOriginalCursor: string | undefined;
requestAnimationFrame(() => {
if (element && prevElement !== element) {
elementOriginalCursor = element.style.cursor;
element.style.cursor = cursor ?? '';
}
});
// Then process the previous element, not so critical
requestAnimationFrame(() => {
if (prevElement && prevElement !== element) {
prevElement.style.cursor = prevElementOriginalCursor ?? '';
prevElementOriginalCursor = elementOriginalCursor;
}
prevElement = element;
});
};
export const resetTemporaryCursor = () => {
if (prevElement) {
prevElement.style.cursor = prevElementOriginalCursor ?? '';
prevElementOriginalCursor = undefined;
prevElement = undefined;
}
};
just call setTemporaryCursor while user moves mouse, and call resetTemporaryCursor() when drag process is wrapped up (on MouseUp for instance).
This does the job for me. The use of requestAnimationFrame is optional, and probably could be improved with experimentation.

Categories