Maquette JS Conditional CSS - maquette

I am trying to create an array of 'li' elements that hold icons for app level navigation. I have a css class "is-active" that highlights the active 'li' element in the list indicating the active location in the nav schema. I expect Maquette JS to render the appropriate icon as 'highlighted' when it is selected by the end user. I am trying to use the classes:{} property as such:
"classes": this._activeNavElementId === navItem.id ? "nav-menu-item nav-menu-item-is-active" : "nav-menu-item", ...
Clearly this is an improper use. The tutorial provides an example where the boolean determines if a class is in the classList; however, I actually need to use classList A if true or classList B if false.
Having a hard time finding good examples of maquettejs conditional css. Any thoughts?

Conditional CSS classes work as follows:
h("li.nav-menu-item", {
"classes": {
"nav-menu-item-is-active": this._activeNavElementId === navItem.id
}
}

Related

Pseudoclass on child conditional on data attribute of parent

I am trying to disable a button dynamically based on a data attribute that's present on the body, code looks sort of like this:
<body data-online="true">
<button disabled></button>
</body>
What I want is to set the pseudoclass disabled based on the value of the body's data attribute. I'm looking for the simplest possible way to do this. I know that conventionally this would be done asynchronously with JS, but for annoying reasons I have no direct control over I would prefer another way. I'm wondering if it's possible to set the pseudoclass directly through CSS or HTML in some way?
I honestly don't this it is possible to achieve this without any JavaScript since the disabled properly is a boolean attribute.
You'll need at least to grab the element using JavaScript and conditionally apply the disabled attribute. As on the code below:
function checkButtonDisabled() {
const body = document.querySelector('body');
const button = document.querySelector('#btn')
const buttonIsDisabled = body.getAttribute('data-online') === 'true'
if (buttonIsDisabled) {
button.setAttribute("disabled", true)
return
}
button.removeAttribute("disabled")
}
checkButtonDisabled()
Although, If your intention is also to style it, you could use the selector below or some variant that could suit better for you:
body[data-online="true"] > button {
/* Your styles here */
}
you could check this article also which explains attribute selectors.

How should I access generated children of a custom HTML component in an idiomatic React way?

I am attempting to create a search bar using a custom HTML component for predictive text input. The way this component is built, it generates several plain HTML children that I need to act on to get full features. Specifically, I need to execute a blur action on one of the generated elements when the user presses escape or enter.
I got it to work using a ref on the custom component and calling getElementsByClassName on the ref, but using getElementsByClassName does not seem like the best solution. It pierces through the virtual and has odd side effects when testing.
This is a snippet of the component being rendered:
<predictive-input id='header-search-bar-input' type='search'
value={this.state.keywords}
ref={(ref: any) => this.predictiveInput = ref}
onKeyDown={(e: React.KeyboardEvent<any>) => this.handleKeyDown(e)}>
</predictive-input>
and the keyDown handler:
private handleKeyDown(e: React.KeyboardEvent<any>) {
// must access the underlying input element of the kat-predictive-input
let input: HTMLElement = this.predictiveInput.getElementsByClassName('header-row-text value')[0] as HTMLElement;
if (e.key === 'Escape') {
// blur the predictive input when the user presses escape
input.blur();
} else if (e.key === 'Enter') {
// commit the search when user presses enter
input.blur();
// handles action of making actual search, using search bar contents
this.commitSearch();
}
}
The element renders two children, one for the bar itself and one for the predictive dropdown. The classes of the underlying in the first are 'header-row-text' and 'value', so the element is correctly selected, but I am worried that this is violating proper React style.
I am using React 16.2, so only callback refs are available. I would rather avoid upgrading, but if a 16.3+ solution is compelling enough, I could consider it.
If you don't have any control over the input then this is the best approach in my opinion.
It's not ideal, but as you're stuck with a 3rd party component you can only choose from the methods that are available to you. In this case, your only real options are to find the element based on its class, or its position in the hierarchy. Both might change if the package is updated, but if I had to choose which would be more stable, I'd go for className.

DOM Selector to get text of element ignoring children?

I'm using the Tab Modifier plugin for Chrome to dynamically rename some tabs that I use daily. In the tab Title definition, it says the following:
You can inject any DOM content with {selector}. Examples: {title} for website title, {h1}, {#id}, {.class}, etc.
Here is an example of the element I want to use to name the tab:
<td class="portalTitleInfoVal">
PORTALNAME
<a class="portalLink">Change Portal</a>
</td>
This is what I'm currently using for the title:
{.portalTitleInfoVal:nth-of-type(4)}
But, of course, the tab is named PORTALNAMEChange Portal.
How can I modify the DOM selector so that the tab is just named "PORTALNAME"?
I know I'm really late to the party, but I found this post while searching for an answer.
I'm working with a lot of old systems and all the tabs just says {title}, which is.. not useful when having 15-20 tabs open at once, and it's tedious to hard code every tab.
So.. I brute forced tested until I found a solution:
Every page has a breadcrumb:
<div class="breadcrumb noPrint">
Home "ยป"
Materials
123123
</div>
So they might have updated the extension since, but your guess was very close. I don't know why you were putting in 4, but I assume you had more elements than posted.
Anyhow, the way I got it to work were by:
{.breadcrumb :nth-last-child(2)} : {.breadcrumb :last-child}
So, there has to be a space between the .class and the child element, which in my case returns Materials : 12312
I haven't tried nearly half, but DoFactorys list of CSS selectors were a big help for me.
The element's first child node is the plain text, before the HTML element (<a>).
$('. portalTitleInfoVal')[0].childNodes[0].nodeValue
It looks like this plugin will only allow for CSS style element selectors compatible with querySelector. It then grabs the text from that element. From their github repo:
/**
* Returns the text related to the given CSS selector
* #param selector
* #returns {string}
*/
getTextBySelector = function (selector) {
var el = document.querySelector(selector), value = '';
if (el !== null) {
value = el.innerText || el.textContent;
}
return value.trim();
};

How to access more than 2 DOM elements "The AngularJS way"?

I'm starting to learn angularJS better, and I've noticed that AngularJS tries to make strong emphasis on separating the view from the controller and encapsulation. One example of this is people telling me DOM manipulation should go in directives. I kinda got the hang of it now, and how using link functions that inject the current element allow for great behavior functionality, but this doesn't explain a problem I always encounter.
Example:
I have a sidebar I want to open by clicking a button. There is no way to do this in button's directive link function without using a hard-coded javascript/jquery selector to grab the sidebar, something I've seen very frowned upon in angularJS (hard-coding dom selectors) since it breaks separation of concerns. I guess one way of getting around this is making each element I wish to manipulate an attribute directive and on it's link function, saving a reference it's element property into a dom-factory so that whenever a directive needs to access an element other than itself, it can call the dom-factory which returns the element, even if it knows nothing where it came from. But is this the "Angular way"?
I say this because in my current project I'm using hard-coded selectors which are already a pain to mantain because I'm constantly changing my css. There must be a better way to access multiple DOM elements. Any ideas?
There are a number of ways to approach this.
One approach, is to create a create a sidebar directive that responds to "well-defined" broadcasted messages to open/close the sidebar.
.directive("sidebar", function(){
return {
templateUrl: "sidebar.template.html",
link: function(scope, element){
scope.$root.$on("openSidebar", function(){
// whatever you do to actually show the sidebar DOM content
// e.x. element.show();
});
}
}
});
Then, a button could invoke a function in some controller to open a sidebar:
$scope.openSidebar = function(){
$scope.$root.$emit("openSidebar");
}
Another approach is to use a $sidebar service - this is somewhat similar to how $modal works in angularui-bootstrap, but could be more simplified.
Well, if you have a directive on a button and the element you need is outside the directive, you could pass the class of the element you need to toggle as an attribute
<button my-directive data-toggle-class="sidebar">open</button>
Then in your directive
App.directive('myDirective', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
angular.element('.' + attrs.toggleClass).toggleClass('active');
}
};
}
You won't always have the link element argument match up with what you need to manipulate unfortunately. There are many "angular ways" to solve this though.
You could even do something like:
<div ng-init="isOpen = false" class="sidebar" ng-class="{'active': isOpen}" ng-click="isOpen = !isOpen">
...
</div>
The best way for directive to communicate with each other is through events. It also keeps with the separation of concerns. Your button could $broadcast on the $rootScope so that all scopes hear it. You would emit and event such as sidebar.open. Then the sidebar directive would listen for that event and act upon it.

Which HTML elements can receive focus?

I'm looking for a definitive list of HTML elements which are allowed to take focus, i.e. which elements will be put into focus when focus() is called on them?
I'm writing a jQuery extension which works on elements that can be brought into focus. I hope the answer to this question will allow me to be specific about the elements I target.
There isn't a definite list, it's up to the browser. The only standard we have is DOM Level 2 HTML, according to which the only elements that have a focus() method are
HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement and HTMLAnchorElement. This notably omits HTMLButtonElement and HTMLAreaElement.
Today's browsers define focus() on HTMLElement, but an element won't actually take focus unless it's one of:
HTMLAnchorElement/HTMLAreaElement with an href
HTMLInputElement/HTMLSelectElement/HTMLTextAreaElement/HTMLButtonElement but not with disabled (IE actually gives you an error if you try), and file uploads have unusual behaviour for security reasons
HTMLIFrameElement (though focusing it doesn't do anything useful). Other embedding elements also, maybe, I haven't tested them all.
Any element with a tabindex
There are likely to be other subtle exceptions and additions to this behaviour depending on browser.
Here I have a CSS-selector based on bobince's answer to select any focusable HTML element:
a[href]:not([tabindex='-1']),
area[href]:not([tabindex='-1']),
input:not([disabled]):not([tabindex='-1']),
select:not([disabled]):not([tabindex='-1']),
textarea:not([disabled]):not([tabindex='-1']),
button:not([disabled]):not([tabindex='-1']),
iframe:not([tabindex='-1']),
[tabindex]:not([tabindex='-1']),
[contentEditable=true]:not([tabindex='-1'])
{
/* your CSS for focusable elements goes here */
}
or a little more beautiful in SASS:
a[href],
area[href],
input:not([disabled]),
select:not([disabled]),
textarea:not([disabled]),
button:not([disabled]),
iframe,
[tabindex],
[contentEditable=true]
{
&:not([tabindex='-1'])
{
/* your SCSS for focusable elements goes here */
}
}
I've added it as an answer, because that was, what I was looking for, when Google redirected me to this Stackoverflow question.
EDIT: There is one more selector, which is focusable:
[contentEditable=true]
However, this is used very rarely.
$focusable:
'a[href]',
'area[href]',
'button',
'details',
'input',
'iframe',
'select',
'textarea',
// these are actually case sensitive but i'm not listing out all the possible variants
'[contentEditable=""]',
'[contentEditable="true"]',
'[contentEditable="TRUE"]',
'[tabindex]:not([tabindex^="-"])',
':not([disabled])';
I'm creating a SCSS list of all focusable elements and I thought this might help someone due to this question's Google rank.
A few things to note:
I changed :not([tabindex="-1"]) to :not([tabindex^="-"]) because it's perfectly plausible to generate -2 somehow. Better safe than sorry right?
Adding :not([tabindex^="-"]) to all the other focusable selectors is completely pointless. When using [tabindex]:not([tabindex^="-"]) it already includes all elements that you'd be negating with :not!
I included :not([disabled]) because disabled elements can never be focusable. So again it's useless to add it to every single element.
The ally.js accessibility library provides an unofficial, test-based list here:
https://allyjs.io/data-tables/focusable.html
(NB: Their page doesn't say how often tests were performed.)
Maybe this one can help:
function focus(el){
el.focus();
return el==document.activeElement;
}
return value: true = success, false = failed
Reff:
https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/activeElement
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
There is a much more elegant way to handle this:
Extend the element prototype like the sample below.
Then you can use it like:
element.isFocusable()
*Returns true if "element" is focusable and false if not.
/**
* Determining if an element can be focused on
* #return {Boolean}
*/
HTMLElement.prototype.isFocusable = function () {
var current = document.activeElement
if (current === this) return true
var protectEvent = (e) => e.stopImmediatePropagation()
this.addEventListener("focus", protectEvent, true)
this.addEventListener("blur", protectEvent, true)
this.focus({preventScroll:true})
var result = document.activeElement === this
this.blur()
if (current) current.focus({preventScroll:true})
this.removeEventListener("focus", protectEvent, true)
this.removeEventListener("blur", protectEvent, true)
return result
}
// A SIMPLE TEST
console.log(document.querySelector('a').isFocusable())
console.log(document.querySelector('a[href]').isFocusable())
<a>Not focusable</a>
Focusable