Material UI Drawer component adding unwanted shadow when not focused - html

I'm having some issues using the Material UI Drawer component. When I try to display it in my webpage it tries to force the focus to the inner div and adds a shadow or border to the component if you're focused anywhere else.
Does anyone know what's causing this shadow to appear and how to disable it? Example screenshot below - you'll see a blue edge at the bottom (this is the same all the way round if I resize the element)
As soon as you click on content inside the Drawer e.g. a List element the shadow goes away. I assume it must be something to do with the component being modal?
<Drawer PaperProps={{ className: classes.floatingMenu }} anchor='top' open onClose={() => {}}>
<div className={classes.dummy}>
</div>
</Drawer>
Note: The floatingMenu class only adds a margin at the top of 55px (i.e. the height of the AppBar - nothing else).

This is somewhat tricky, but possible with just some props and styling. The element that is shading the rest of the UI is the Backdrop component of the Modal component. Drawer uses Modal when it's in temporary mode. The hideBackdrop prop of the Modal controls whether the shade is seen or not, and you can pass this prop to the Drawer directly as well.
However, the Modal component itself would still cover the whole viewport, preventing you from clicking other areas of the UI, before the Drawer is closed. I'm not sure if there's a simpler way, but at least you can do it by just using CSS styling to make the Modal element "through-clickable" by setting its pointer-events to none. To restore the "clickability" of the drawer itself, you should then set its pointer-events back to all.
So, for instance, just using the style prop to make simple inline styling:
<Drawer hideBackdrop style={{ pointerEvents: 'none' }}>
<div style={{ pointerEvents: 'all' }}>
I'm a sidebar!
</div>
</Drawer>
A working example
If you twiddle with Material UI's styling solution, you could also pass the pointer-events style rule to the Drawer's own Paper through the classes prop.

I was able to fix the issue in a little more simplistic manner after reading the doc's a bit more. On the Modal page it states:
Notice that you can disable the outline (often blue or gold) with the
outline: 0 CSS property.
Based on this I didn't touch the component or inner components from my question but instead I simply added one extra CSS class outline: 0 to floatingMenu (which is already passed to the PaperProps):
floatingMenu: {
marginTop: '55px',
outline: 0
}
This resolved the problem and I no longer see the blue shadow border.

Related

Click on Dropdown moves the body of page. primeng

I have 2 dropdowns in Datatables to fiter the content and i've got a bug. When a dropdown on the right side of the page, it shifts the page to the left by about 10px. And in the opposite case when dropdown on the left it shifts page to the right. With first Click happens nothig, but only when i try to change the selected value.
<p-dropdown
id="ListFilter_priority_dropdown"
inputId="ListFilter_priority_dropdown_input" #priorityDropdown
styleClass="my-form-control" [options]="prioritySelectItems"
[showClear]="true" placeholder="Select"
(onChange)="filter($event.value)" appendTo="body"> <ng-template
let-option pTemplate="item"> <div
id="ListFilter_priority_item_container"> <span
id="ListFilter_priority_dropDown_{{option.label}}">{{option.label}}</span>
</div> </ng-template> </p-dropdown>
Angular v 13.3.8
PrimeNG version 13.4.1
Build / Runtime - Angular CLI App
I have already tried to play around the position of the "body" - absolute, static, relative and s.o. but it doesn't work aswell as changing the property "appendTo" of dropdown.
This seems like an unresolved bug with the p-dropdown component of PrimeNG. Other people are having this issue:
https://github.com/primefaces/primeng/issues/10992
https://github.com/primefaces/primeng/issues/11046
However, I was able to overcome the problem in PrimeNG 14.1.2 by adding [virtualScroll]="true" to the p-dropdown component, and also adding a min-width to the dropdown panel with the property [panelStyle]="{ 'min-width': 'min(100vw, 250px)' }"
Another situation that may be causing this is if you're using appendTo="body" and the body has overflow: hidden or overflow-x: hidden but has horizontally overflowing children.
If you're using appendTo="body", try setting the overflow to auto and check if you have a horizontal scrollbar.
If you do, this may be causing the problem. To fix this, either take care of the overflowing children, or set overflow: hidden/overflow-x: hidden on a child element of the body.

Position SVG lines over HTML elements, while still allowing interaction with the HTMl elements [duplicate]

I have a div that has background:transparent, along with border. Underneath this div, I have more elements.
Currently, I'm able to click the underlying elements when I click outside of the overlay div. However, I'm unable to click the underlying elements when clicking directly on the overlay div.
I want to be able to click through this div so that I can click on the underlying elements.
Yes, you CAN do this.
Using pointer-events: none along with CSS conditional statements for IE11 (does not work in IE10 or below), you can get a cross browser compatible solution for this problem.
Using AlphaImageLoader, you can even put transparent .PNG/.GIFs in the overlay div and have clicks flow through to elements underneath.
CSS:
pointer-events: none;
background: url('your_transparent.png');
IE11 conditional:
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='your_transparent.png', sizingMethod='scale');
background: none !important;
Here is a basic example page with all the code.
Yes, you CAN force overlapping layers to pass through (ignore) click events.
PLUS you CAN have specific children excluded from this behavior...
You can do this, using pointer-events
pointer-events influences the reaction to click-, tap-, scroll- und hover events.
In a layer that should ignore / pass-through mentioned events you set
pointer-events: none;
Children of that unresponsive layer that need to react mouse / tap events again need:
pointer-events: auto;
That second part is very helpful if you work with multiple overlapping div layers (probably some parents being transparent), where you need to be able to click on child elements and only that child elements.
Example usage:
.parent {
pointer-events:none;
}
.child {
pointer-events:auto;
}
<div class="parent">
I'm unresponsive
I'm clickable again, wohoo !
</div>
Allowing the user to click through a div to the underlying element depends on the browser. All modern browsers, including Firefox, Chrome, Safari, and Opera, understand pointer-events:none.
For IE, it depends on the background. If the background is transparent, clickthrough works without you needing to do anything. On the other hand, for something like background:white; opacity:0; filter:Alpha(opacity=0);, IE needs manual event forwarding.
See a JSFiddle test and CanIUse pointer events.
I'm adding this answer because I didn’t see it here in full. I was able to do this using elementFromPoint. So basically:
attach a click to the div you want to be clicked through
hide it
determine what element the pointer is on
fire the click on the element there.
var range-selector= $("")
.css("position", "absolute").addClass("range-selector")
.appendTo("")
.click(function(e) {
_range-selector.hide();
$(document.elementFromPoint(e.clientX,e.clientY)).trigger("click");
});
In my case the overlaying div is absolutely positioned—I am not sure if this makes a difference. This works on IE8/9, Safari Chrome and Firefox at least.
Hide overlaying the element
Determine cursor coordinates
Get element on those coordinates
Trigger click on element
Show overlaying element again
$('#elementontop').click(e => {
$('#elementontop').hide();
$(document.elementFromPoint(e.clientX, e.clientY)).trigger("click");
$('#elementontop').show();
});
I needed to do this and decided to take this route:
$('.overlay').click(function(e){
var left = $(window).scrollLeft();
var top = $(window).scrollTop();
//hide the overlay for now so the document can find the underlying elements
$(this).css('display','none');
//use the current scroll position to deduct from the click position
$(document.elementFromPoint(e.pageX-left, e.pageY-top)).click();
//show the overlay again
$(this).css('display','block');
});
I currently work with canvas speech balloons. But because the balloon with the pointer is wrapped in a div, some links under it aren't click able anymore. I cant use extjs in this case.
See basic example for my speech balloon tutorial requires HTML5
So I decided to collect all link coordinates from inside the balloons in an array.
var clickarray=[];
function getcoo(thatdiv){
thatdiv.find(".link").each(function(){
var offset=$(this).offset();
clickarray.unshift([(offset.left),
(offset.top),
(offset.left+$(this).width()),
(offset.top+$(this).height()),
($(this).attr('name')),
1]);
});
}
I call this function on each (new) balloon. It grabs the coordinates of the left/top and right/down corners of a link.class - additionally the name attribute for what to do if someone clicks in that coordinates and I loved to set a 1 which means that it wasn't clicked jet. And unshift this array to the clickarray. You could use push too.
To work with that array:
$("body").click(function(event){
event.preventDefault();//if it is a a-tag
var x=event.pageX;
var y=event.pageY;
var job="";
for(var i in clickarray){
if(x>=clickarray[i][0] && x<=clickarray[i][2] && y>=clickarray[i][1] && y<=clickarray[i][3] && clickarray[i][5]==1){
job=clickarray[i][4];
clickarray[i][5]=0;//set to allready clicked
break;
}
}
if(job.length>0){
// --do some thing with the job --
}
});
This function proofs the coordinates of a body click event or whether it was already clicked and returns the name attribute. I think it is not necessary to go deeper, but you see it is not that complicate.
Hope in was enlish...
Another idea to try (situationally) would be to:
Put the content you want in a div;
Put the non-clicking overlay over the entire page with a z-index higher,
make another cropped copy of the original div
overlay and abs position the copy div in the same place as the original content you want to be clickable with an even higher z-index?
Any thoughts?
I think the event.stopPropagation(); should be mentioned here as well. Add this to the Click function of your button.
Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
Just wrap a tag around all the HTML extract, for example
<a href="/categories/1">
<img alt="test1" class="img-responsive" src="/assets/photo.jpg" />
<div class="caption bg-orange">
<h2>
test1
</h2>
</div>
</a>
in my example my caption class has hover effects, that with pointer-events:none; you just will lose
wrapping the content will keep your hover effects and you can click in all the picture, div included, regards!
An easier way would be to inline the transparent background image using Data URIs as follows:
.click-through {
pointer-events: none;
background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
}
I think that you can consider changing your markup. If I am not wrong, you'd like to put an invisible layer above the document and your invisible markup may be preceding your document image (is this correct?).
Instead, I propose that you put the invisible right after the document image but changing the position to absolute.
Notice that you need a parent element to have position: relative and then you will be able to use this idea. Otherwise your absolute layer will be placed just in the top left corner.
An absolute position element is positioned relative to the first parent
element that has a position other than static.
If no such element is found, the containing block is html
Hope this helps. See here for more information about CSS positioning.
You can place an AP overlay like...
#overlay {
position: absolute;
top: -79px;
left: -60px;
height: 80px;
width: 380px;
z-index: 2;
background: url(fake.gif);
}
<div id="overlay"></div>
just put it over where you dont want ie cliked. Works in all.
This is not a precise answer for the question but may help in finding a workaround for it.
I had an image I was hiding on page load and displaying when waiting on an AJAX call then hiding again however...
I found the only way to display my image when loading the page then make it disappear and be able to click things where the image was located before hiding it was to put the image into a DIV, make the size of the DIV 10x10 pixels or small enough to prevent it causing an issue then hiding the containing div. This allowed the image to overflow the div while visible and when the div was hidden, only the divs area was affected by inability to click objects beneath and not the whole size of the image the DIV contained and was displaying.
I tried all the methods to hide the image including CSS display=none/block, opacity=0, hiding the image with hidden=true. All of them resulted in my image being hidden but the area where it was displayed to act like there was a cover over the stuff underneath so clicks and so on wouldn't act on the underlying objects. Once the image was inside a tiny DIV and I hid the tiny DIV, the entire area occupied by the image was clear and only the tiny area under the DIV I hid was affected but as I made it small enough (10x10 pixels), the issue was fixed (sort of).
I found this to be a dirty workaround for what should be a simple issue but I was not able to find any way to hide the object in its native format without a container. My object was in the form of etc. If anyone has a better way, please let me know.
I couldn't always use pointer-events: none in my scenario, because I wanted both the overlay and the underlying element(s) to be clickable / selectable.
The DOM structure looked like this:
<div id="outerElement">
<div id="canvas-wrapper">
<canvas id="overlay"></canvas>
</div>
<!-- Omitted: element(s) behind canvas that should still be selectable -->
</div>
(The outerElement, canvas-wrapper and canvas elements have the same size.)
To make the elements behind the canvas act normally (e.g. selectable, editable), I used the following code:
canvasWrapper.style.pointerEvents = 'none';
outerElement.addEventListener('mousedown', event => {
const clickedOnElementInCanvas = yourCheck // TODO: check if the event *would* click a canvas element.
if (!clickedOnElementInCanvas) {
// if necessary, add logic to deselect your canvas elements ...
wrapper.style.pointerEvents = 'none';
return true;
}
// Check if we emitted the event ourselves (avoid endless loop)
if (event.isTrusted) {
// Manually forward element to the canvas
const mouseEvent = new MouseEvent(event.type, event);
canvas.dispatchEvent(mouseEvent);
mouseEvent.stopPropagation();
}
return true;
});
Some canvas objects also came with input fields, so I had to allow keyboard events, too.
To do this, I had to update the pointerEvents property based on whether a canvas input field was currently focused or not:
onCanvasModified(canvas, () => {
const inputFieldInCanvasActive = // TODO: Check if an input field of the canvas is active.
wrapper.style.pointerEvents = inputFieldInCanvasActive ? 'auto' : 'none';
});
it doesn't work that way. the work around is to manually check the coordinates of the mouse click against the area occupied by each element.
area occupied by an element can found found by 1. getting the location of the element with respect to the top left of the page, and 2. the width and the height. a library like jQuery makes this pretty simple, although it can be done in plain js. adding an event handler for mousemove on the document object will provide continuous updates of the mouse position from the top and left of the page. deciding if the mouse is over any given object consists of checking if the mouse position is between the left, right, top and bottom edges of an element.
Nope, you can't click ‘through’ an element. You can get the co-ordinates of the click and try to work out what element was underneath the clicked element, but this is really tedious for browsers that don't have document.elementFromPoint. Then you still have to emulate the default action of clicking, which isn't necessarily trivial depending on what elements you have under there.
Since you've got a fully-transparent window area, you'll probably be better off implementing it as separate border elements around the outside, leaving the centre area free of obstruction so you can really just click straight through.

tabindex: unable to focus elements that are visible only on hover [duplicate]

I have a component that, upon a hover, shows a button and a link that you can click on. This is not a menu... just a box in the middle of the page.
For accessibility, I would like a user to be able to tab into the container (happens now, and displays the content in the .HiddenUntilHover class) AND also continue to tab to the button and link that show up on the hover/focused state.
Right now you can focus on the container and see the hover state; however, when you tab it just goes to the next element and does not allow you to tab to the button or link WITHIN the hover state.
Pseudo code example:
/* My component .jsx */
<div tabIndex="0" className="MainContainer">
<div className="SomeOtherClass">
<div className="HiddenUntilHover">
/* I would like to be able to tab to these clickable things! */
<button>Click me!</button>
I am also clickable
</div>
</div>
</div>
And my SCSS:
.HiddenUntilHover {
display: none;
}
MainContainer:focus,
MainContainer:hover,
> .HiddenUntilHover {
display: block
}
I ran into this issue a few days ago and I solved it using css classes to make the hovered content accessible via keyboard navigation.
The way I got this working was to use css pseudo-classes to ensure that when the div element is active & focused that the buttons inside also display. Specifically the additional use of :focus-within & :focus-visible should ensure that when you tab over the list items, their contents are also displayed and keyboard accessible.
.MainContainer {
&:not(:hover, :focus, :active, :focus-visible, :focus-within) {
.HiddenUntilHover {
visibility: hidden;
}
}
}
<body>
<div tabIndex="0" className="MainContainer">
Content
<div className="SomeOtherClass">
<div className="HiddenUntilHover">
<button>Click me!</button>
I am also clickable
</div>
</div>
</div>
</body>
Here's a link to the Codesandbox demo of this working
When the box is in focus, tabbing further to the button will make the box blur, which will hide it, and its contents, so focus will move to the next accessible element. I think this is the behavior you are experiencing.
You might consider using inserting an aria-activedescendant or tabindex attribute when the box comes into focus. This requires a little javascript.
Strictly speaking, you don't need to rely on the hover state to make that control accessible. You could have an offscreen (or clipped) button/link that is not a DOM child of the hidden (display:none) box. If you take this approach, read up on the aria-owns attribute.
As long as it is marked up as a button or link (or has a tabindex="0" setting), and is not 'really' hidden, it ought to be possible to tab to it.
Try increasing the properties of the class MainContainer
for example.
.MainContainer {
width: 100%;
height: 100px;
}
.MainContainer .HiddenUntilHover {
display: none;
}
.MainContainer:hover .HiddenUntilHover, .MainContainer:focus .HiddenUntilHover {
display: block;
}
Elements appearing on hover are inherently inaccessible. You are experiencing one side of the problem with your code, where it is difficult to make it keyboard accessible.
But think about touch screens that have no real concept of hover: is there some way to reach your button on a smarphone or tablet?
For a more pragmatic answer, if you need to stay with hover, a less hacky solution than the two already posted ones could be the following:
use focusin and focusout events. See for example this question for explanations and differences with focus/blur, and this w3school doc for browser compatibility.
You will have to structure your HTML differently, such as:
<div id="outer">
<div id="hover">
...
</div><!--hover-->
<button>Your button which only appears on hover</utton>
</div><!--outer-->
As well as use a bit of js:
$('#outer').on('focusin', __=>$('#hover').classNames.add('keep-visible'));
$('#outer').on('focusout', __=>$('#hover').classNames.remove('keep-visible'));
With a corresponding .keep-visible class which will leave the element display:block (I'm not a CSS expert, I let you write the code).
The overal functionning is the following: when some element within #outer takes the focus, the focusin element is fired due to bubbling. In the event, you put your class .keep-visible which makes the element to stay visible.
The focusout event is fired when the focus leaves the last element within #outer. At that point you remove the .keep-visible class, which makes the element to disappear.
According to the link above, onfocusin/out aren't standard, but are supported by all major browsers including IE. Firefox is the last one to implement it in 52.0, so it's a kind of defacto standard; we can reasonably expect that it won't disappear soon.

Angular Material 2: Sidenav has no backdrop

I am currently developing an Angular 2 site and having some trouble with the SideNav component.
The SideNav can have 3 modes, none of which seem to change what happens when I open the Sidenav. I am trying to get the backdrop to display after opening.
The sideNav does open, however the backdrop doesn' t show up.
The root of the app is the md-sidenav container. The SideBar component will also be passed to the NavBar component as the open button is defined in there.
Note that I have also tried to bring the SideBar component outside the <md-sidenav-container>
<md-sidenav-container>
<sidebar #sideBarComponent></sidebar>
<navbar [sideBar]=sideBarComponent></navbar>
<router-outlet></router-outlet>
</md-sidenav-container>
The SidNnav currently contains 2 links and is set to mode "over" (which should create a SideNav and backdrop over the main content)
<md-sidenav #sideBar mode="over">
<md-nav-list>
<a md-list-item routerLink="/home" (click)="sideBar.close()" routerLinkActive="active-link">
<md-icon md-list-icon>home</md-icon>
<p md-line>Home</p>
</a>
<a md-list-item routerLink="/users" (click)="sideBar.close()" routerLinkActive="active-link">
<md-icon md-list-icon>account_circle</md-icon>
<p md-line>Users</p>
</a>
</md-nav-list>
</md-sidenav>
Inspecting the page using chrome's developer tools reveals that a backdrop is created (<div class="md-sidenav-backdrop"></div>) and covering the whole page, however it is set to hidden and has no effect due to not having a functional CSS applied to it. (only set fixed width, height and position, nothing visible that alters the page)
The prebuild-css has a background color tag for it, however it needs a .md-sidenav-shown:
.md-sidenav-backdrop.md-sidenav-shown {
background-color: rgba(0, 0, 0, .6)
}
Is there anything that would cause this behavior to happen?
None of the tutorials and examples I found did something complicated so I think it is due to my set-up.
Relevant Libraries and versions (All webjars):
Angular 2.4.1
common
core
compiler
forms
http
platform-browser
platform-browser-dynamic
router
Angular-Material 2.0.0-Beta.1
SystemJs 0.19.41
Core-js 2.4.1
Reflect-Metadata 0.1.8
Typescript 2.1.4
RxJs 5.0.1
HammerJS 2.0.6
Zone.js 0.7.4
It seems that you cannot put md-sidenav inside component because there is no link between md-sidenav-container and child md-sidenav's. Md-sidenav-container uses #ContentChildren to access md-sidenav instances.
So there must be direct parent-child relation between md-sidenav-container and md-sidenav.
To keep the scope of CSS defined in your.component.css limited to only that component, Angular processes the CSS/SCSS/SASS files and adds "attribute" selectors to each CSS rule defined in that file.
In your case, it converts your .md-sidenav-backdrop.md-sidenav-shown {...} CSS rule into something like:
.md-sidenav-backdrop.md-sidenav-shown[_ngcontent-xyh-65] {
background-color: rgba(0, 0, 0, .6)
}
Angular is strict about restricting the scope. The trouble is that the backdrop <div class="md-sidenav-backdrop"></div> is added by the material component (at browser render time, I presume). Hence, the selector suffix [_ngcontent-xyh-65] is not added to that element at compile time.
The only way around it, that I could find till now, is to move your CSS to <root>/src/styles.css.
UPDATE: As Rose suggests, it would be safest to have your component name selector prefixed to the CSS you add in styles.css. In your case:
md-sidenav-container > .md-sidenav-backdrop.md-sidenav-shown {
background-color: rgba(0, 0, 0, .6)
}

How to remove CSS mousedown effects

I have this problem in Safari and Chrome but not in IE.
When I click a button the mousedown event triggers some kind of CSS rule which makes it slightly wider.
Because of this it drops down onto the next row and the click event is not triggered.
It stays on the next row until the mouse button is released.
I'm working on a large existing site and it's difficult to isolate all the CSS, but I think this could be due to an effect inherent in the browser(s).
Is there a CSS way to stop any effects occuring when the button is clicked?
Thanks for your help.
This is the CSS I have found for :active / :hover.
I don't think this could cause it!
a:hover, a:active
{
text-decoration: none;
}
(The button is an image inside an anchor)
Open your page with Chrome. Right click on the element and select inspect element. On the right handside corner of the inspect element handler, you will see few icons.
Click on the middle one(Which is having a arrow. When you hover it a label will display as "Toggle element State").
Change the element state to active (and to focus if it didn't change anything), and now you will be able to see what css rules are used to apply those changes to your button(It can be a padding or width).
Since now you know what the rule is, you can undo it using another rule (Or using javascript). It's hard to say how to remove the effects without knowing what the effects are.
you can declare a class in css name it for exemple abortmousedowncss :
.abortmousedowncss{
width:yourwidth; !important /* this dont allow any css to overide it ;)*/
}
and you can apply it after with jquery like this :
$('#yourbutton').addClass("abortmousedowncss");