Prime NG Menu - Focus on first element when activated - html

I am attempting to move the focus from the prime ng menu to the first of the list elements that appear when you toggle the menu. Here is what I have so far.
In the template I have:
<p-menu appendTo="body" #menu [popup]="true" [model]="menuItems" (onShow)="openMenu()"></p-menu>
And in my typescript I have:
#ViewChild('menu', { read: ElementRef }) menu: ElementRef;
...
openMenu() {
const menuItem = (
this.menu.nativeElement.getElementsByClassName(
'p-menuitem'
) as HTMLCollectionOf<HTMLElement>
)[0];
menuItem.focus();
}
When testing this, the element is definitely being discovered, and I can set attributes on the element. However, setting the focus does not appear to be working. Are there any decent ways of accessing DOM elements and setting focus?
Thanks!

Here is what worked for me in the end:
openMenu() {
const menuItem = (
this.menu.nativeElement.getElementsByClassName(
'p-menuitem-link'
) as HTMLCollectionOf<HTMLElement>
)[0];
setTimeout(() => {
menuItem.focus();
}, 1);
}

Related

Change color of popup (angular, TS)

I build a custom popup which is visible when I click a button. When I click somewhere else on the document the popup should be closed / invisible.
That works pretty well.
Now I want to change the style property of this popup. The problem is that i cant change it.
The code below returns that the HTML object is null but if i click another buttom with same functionality the style changes.
Thats my code so far
tooltip.component.ts
export class TooltipComponent implements OnInit {
popup = false;
// open popup
openToolTip($event: {
target: any; stopPropagation: () => void;
})
{
$event.stopPropagation();
this.popup = !this.popup;
testvariable = document.getElementByID("popupId");
testvariable.style.backgroundcolor = "green"; //backgroundcolor just for testing
}
}
// close popup if clicked on document
#HostListener('document:click', ['$event']) onDocumentClick(event: any) {
this.popup = false;
}
constructor() { }
ngOnInit(): void {
}
}
html
<span class="info-icon" (click)="openToolTip($event)">
<mat-icon>info_outline</mat-icon>
</span>
<div *ngIf="popup" id="popupId" class="popup" (click)="$event.stopPropagation()">
<div class="text-box">
</div>
<!-- close-button -->
<a class="close" (click)="popup = false">×</a>
</div>```
EDIT:
I used the timeout function like Elikill58 said. Its a workaround but it solves my problem for now :)
The problem comes from the element isn't known yet. You are checking it too fast. There is multiple way to fix it:
Wait for it.
You can use the timeout function:
timeout(function() {
var testvariable = document.getElementByID("popupId");
testvariable.style.backgroundcolor = "green";
}, 0);
Set style with ngStyle.
If the style should depend of values, you can do like:
<div [ngStyle]="{'background-color': something ? 'green' : 'red'}">
</div>
Change style by default.
This will change the style for all popup, without requiring JS manipulation:
.popup {
background-color: green;
}
Change style with ID.
If you are using specific ID, you can do like:
#popupId {
background-color: green;
}
All ways have advantages and disadvantages, you should take the one that correspond to what you are looking for.

Pivot icons are not clickable

I have a pivot element in my page, it's work but when I want to change text by icons, they become not clickable and we have to click in the grey part. Do you know how make them clickable ?
In green the clickable part and in red not clickable part.
Part of my code :
<li id="listPivotAccount" class="ms-Pivot-link is-selected " data-content="account" title="Mon compte" tabindex="1">
<i style="" class=" ms-Icon ms-Icon--Accounts" aria-hidden="true"></i>
</li>
You can check the code here
For the record, I have never used SharePoint, so there may be a more elegant solution.
You can fix this behaviour by adding this vanilla JavaScript after your current JavaScript:
// select all icons
var msIcons = document.querySelectorAll(".ms-Icon");
// loop all icons
for (var i = 0; i < msIcons.length; i++) {
// add a click event to the nearest element with class "ms-Pivot-link"
msIcons[i].closest(".ms-Pivot-link").addEventListener("click", function() {
this.click();
});
}
jQuery Example of the above code:
$(".ms-Icon").on("click", function() {
$(this).closest(".ms-Pivot-link").click();
});
var Dropdown = new Class({
initialize: function() {
var e = this;
document.addEvents({
"click:relay(.windowLabel, .dropdown a.dropdownTrigger)": function(t, n) {
t && (t.preventDefault(),
t.stopPropagation()), // issue is here
e.showPopover.call(e, n)
}
}),
document.body.addEventListener("click", function(t) {
e.hideOutside.call(e, t)
})
},
// ...
})
Problem is in preventing propagation of events, and as result all nested elements shouldn't emit what you need.
What is the solution?
You can try add the icon in different way (for example using :before, :after)
The simple way to fix it is to trigger the pivot with a click. So if you use JQuery :
$('.ms-Icon').click(function () {
var pivot = $(this).closest(".ms-Pivot-link");
pivot.click();
});
Short and compatible with IE > 9

Vue.js anchor to div within the same component

I'm developing a Vue.js application and I'm having trouble to link an anchor to a certain div within a component.
I have the following anchor:
Porto, Portugal
and the following div:
<div id="porto" class="fl-porto">
I'm using vue-router in hash mode.
The problem is, whenever I click the "porto-button" it will redirect me to the "home" page ( ' / ' )
I'm using Vue.js 1.X and I tried using history mode (URL without the hashbang) but it gives me a cannot GET '/page' error upon refreshing a page.
Am I doing something wrong? What can I do about this?
Because you are using router in hash mode, you will not be able to scroll that easily because scrolling to /#something will actually redirect you to 'something' page.
You will have to emulate scrolling behaviour on your own, try doing something like that:
//P.S. the code is written for Vue 2.
//You will have to adjust it to Vue 1.
//Your view:
<a class="porto-button" #click="scrollMeTo('porto')">Porto, Portugal</a>
...
<div ref="porto" class="fl-porto">
//Your code:
methods: {
scrollMeTo(refName) {
var element = this.$refs[refName];
var top = element.offsetTop;
window.scrollTo(0, top);
}
}
How it works:
Set the references through ref attribute to the element you would like to scroll to;
Write a function that will programmatically set window.scrollY to the top of the referenced element.
Job is done :)
Update 1:
jsfiddle https://jsfiddle.net/5k4ptmqg/4/
Update 2:
Seems that in Vue 1 ref="name" looked like el:name (docs), here is an updated example:
https://jsfiddle.net/5y3pkoyz/2/
Another method is to use "scrollIntoView()"
So, euvl's code still stands, except you would change the method slightly:
new Vue({
el: '#app',
methods: {
goto(refName) {
var element = this.$els[refName];
element.scrollIntoView();
}
}
})
If you wanted to get fancy and make the scroll smooth, you can even add the following:
element.scrollIntoView({ behavior: 'smooth' });
Note that this will need a polyfill for older browsers.
What worked for me
<router-link to="#leaders">Leaders</router-link>
or dynamic
<router-link :to="`#${subMenuItem.linkTarget}`" class="page-submenu-list__link">
{{subMenuItem.linkTitle}}
</router-link>
in router
routes:[],
scrollBehavior (to, from, savedPosition) {
//https://router.vuejs.org/guide/advanced/scroll-behavior.html
if (to.hash) {
return { selector: to.hash }
} else if (savedPosition) {
return savedPosition;
} else {
return { x: 0, y: 0 }
}
}
An alternative solution is to use the v-scroll-to directive (webpage, github).
I find this solution to be clean, simple, flexible and effective. To use:
Install it:
npm install --save vue-scrollto
Have Vue 'use' it:
var VueScrollTo = require('vue-scrollto');
Vue.use(VueScrollTo)
Apply it as a directive in your Vue component's template:
Scroll to #element
<div id="element">
Hi. I'm #element.
</div>
Or apply it programmatically in your Vue component's methods:
this.$scrollTo('#element', 500, { easing: 'ease-in-out' })
Or apply it programmatically in your Vuex actions:
import { scrollTo } from 'vue-scrollto'
scrollTo('#element', 500, { easing: 'ease-in-out' })
Another solution, if you're already using Vuetify, you may prefer to use Vuetify's built-in programmatic scrolling method, $vuetify.goTo():
<v-btn #click="$vuetify.goTo('#element', {duration: 500, easing: 'easeInOutCubic'})">
Scroll to #element
</v-btn>
<div id="element">
Hi. I'm #element.
</div>
If you set a ref="something" on an element, you could also use this oneliner with #click:
<a #click="$refs.something.$el.scrollIntoView()">
Go to something
</a>

Access dom-repeat element's CSS class on tap

My dom repeat displays a list of icons which I can bookmark or unbookmark ,which generating dom-repeat I call a function to find if this icon is bookmarked or not,that will return CSS class
.book-marked {
color: red;
}
.not-book-marked {
color: green;
}
<template is="dom-repeat" items="{{membersList}}">
<iron-icon icon="bookmark" class$="[[_computeBookMark(item.userId)]]" on-tap="_toogleBookMark"></iron-icon>
</template>
Once I get all my list of icon now if user click that icon I need to toogle css class.so I wrote on-tap function
_toogleBookMark:function(e) {
var userData = e.model.item; //gets entire data object of that element
var index = e.model.index; //gets index of that element
},
I can't use ID since its dom-repeat ,Is there any other ways so that I can change CSS of that dom-repeat element in _toogleBookMark() function on clicking? or is it possible to change CSS with index??or using "e" reference in _toogleBookMark(e) function !!
Not sure if I understood correctly - you want to access the element you've tapped?
Just use the event.target property then. It will return the element on which the event happened, in this case, the icon you have tapped.
_toogleBookMark = function(e) {
e.target.classList.toggle("not-book-marked");
}
Check this example.
Mind you:
1) When using Shady DOM, assuming our element is a custom element, target can be a component from the element's template, not the element itself. To prevent that, use Polymer.dom(e).localTarget (read more here).
2) When using a custom element with light DOM children, the above may not be enough, your (local)target will be a light DOM child, not the element you wished for. In that case, use Element.closest(selector) to (optionally) go up the DOM to the element you want. Read more about the method here.
As you just want to swap your class on tap, do it like this:
Add an own attribute, like data-id="1" and the id attribute, but be sure they have the same value:
<template is="dom-repeat" items="{{membersList}}">
<iron-icon icon="bookmark" class$="[[_computeBookMark(item.userId)]]" on-tap="_toogleBookMark" data-id="{{item.userId}}" id="{{item.userId}}"></iron-icon>
</template>
Now, inside your _toggleBookMark function, you can access the current tapped element and swap CSS classes by:
_toogleBookMark:function(e) {
// this gives you your userId from the data-id attribute
var userId = e.path[0].dataId;
// we can access the element now with
var element = this.$$('#' + e.path[0].dataId);
if (element.classList.contains('book-marked')) {
element.classList.remove('book-marked');
element.classList.add('not-book-marked');
} else {
element.classList.add('book-marked');
element.classList.remove('not-book-marked');
}
},

Kendo PanelBar HTML string as Content

I am implementing a view where there are tabs (Kendo TabStrip) and inside these tabs are some accordion items (Kendo PanelBar).
I dinamically draw the tabs using a foreach, and in each tab, I also use a foreach to draw the accordion. The thing is that, the content of each accordion item is a HTML string (like: <p>Some <strong>text</strong></p>).
In chrome all work fine, but with IE8 everything goes out (because the page HTML mixes with the string HTML).
This is my code:
#(Html.Kendo().TabStrip()
.Name("tabAyuda")
.HtmlAttributes(new { style = "" })
.Animation(false)
.SelectedIndex(0)
.Items(tabAyuda =>
{
foreach (KeyValuePair<string, IList<ElementoAyuda>> accion in Model)
{
if (!string.IsNullOrWhiteSpace(accion.Key))
{
tabAyuda.Add().Text(accion.Key)
.Content(#<text>
#(Html.Kendo().PanelBar()
.Name("panelbar" + accion.Key)
.ExpandMode(PanelBarExpandMode.Single)
.Items(panelbar =>
{
foreach (ElementoAyuda elemento in accion.Value)
{
panelbar.Add()
.Text(elemento.Head)
.Content(elemento.Detail);
}
})
)
</text>);
}
}
})
)
I've also tried with this code inside .Content:
.Content(#<text>
#Html.Raw(elemento.Detail)
</text>)
But I get this error: Custom tool error: Inline markup blocks (#<p>Content</p>) cannot be nested. Only one level of inline markup is allowed.
Any advice??
Thanks in advance!
Solved, it was my fault. Some of the HTML strings had invalid syntax, but somehow in Chrome works XD