Ng-class - Active / Inactive button - html

I have a button range with 2 buttons, and I want, when user click on one of them, ng-class add class on button active (My two views are displayed by ng-show/hide).
I have test this, but it's don't work :
HTML :
<div class="button-bar">
<a class="button btn-transpr-left" ng-class="{'activ-btn': isActive1}" ng-click="firstStep()">Acceptées</a>
<a class="button btn-transpr-right" ng-class="{'activ-btn': isActive2}" ng-click="nextStep()">En attente</a>
</div>
CSS :
.activ-btn {
border-bottom: 3px solid !important;
font-weight: bolder !important;
}
JS :
$scope.isActive2 = false;
$scope.isActive1 = true;
$scope.nextStep = function() {
$scope.data.step += 1;
$scope.isActive1 = $scope.isActive1;
$scope.isActive2 = !$scope.isActive2;
}
$scope.firstStep = function() {
$scope.data.step = 1;
$scope.isActive1 = !$scope.isActive1;
$scope.isActive2 = $scope.isActive2;
}
Maybe I make mistake ... Someone can help me ?
Thank's all folk !

Part of the issue was that you weren't defining data on your scope. It was undefined and error-ing out.
$scope.data = { step: 1 }
I've simplified this for you. You don't need to really drive this by explicit flags on your scope, rather you can simply do a check to see if the current step value is what you want on the button in the ng-class expression
ng-class="{'activ-btn':data.step == <desired step value>}"
test - http://codepen.io/jusopi/pen/EPyONL

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.

(Angular) Clicking on a button highlights another button because I'm changing a value in an array. Why?

So I'm trying to make a website using Angular and have come across a very peculiar bug.
Basically I have an *ngFor div that creates 12 buttons.
<div *ngFor = "let color of colors; let i = index" style = "display: inline;">
<button (click) = "toggleTrue(i)" id = "{{i}}" class = "coloredButton">
</button>
</div>
When a button is clicked then it should highlight the button and then change a value in an array within my component like so.
toggleTrue(id: number){
this.colors[id] = !this.colors[id];
var button = document.getElementById(id.toString());
if(this.colors[id] === true){
button.style.border = "10px";
button.style.borderColor = "white";
button.style.borderStyle = "solid";
}
else{
button.style.borderStyle = "none";
}
}
However for some reason whenever I click a button it highlights the button next to it and if I click it twice more than it will highlight a button two buttons away for some reason, click it twice more and it'll highlight a button three buttons away, etc. It's not highlighting the right button, but the id it passes through to the function is correct, I checked using console.log(id).
I've messed around a bit (taking out classes and lines and whatnot) and I've isolated the problem to this line.
this.colors[id] = !this.colors[id];
If I remove this then everything works fine (except of course I can't edit the value of the array anymore). This is proven further when I remove the click function of my buttons and the default highlight for buttons correctly highlights the right button.
Can anyone please help me out and tell me why it's not highlighting correctly? Any help would be appreciated.
If you're curious I created the array earlier within the component using colors = [] and it's an array of booleans that I filled within the constructor using a basic for loop.
https://codesandbox.io/s/crimson-brook-wn3q0?file=/src/app/app.component.html
Here is a sandbox with a solution.
Essentially you want to keep styling in the template through the use of NgClass or NgStyle
and use the component just for changing state, avoid accessing the DOM directly due to lifecycle problems.
Typescript
export class AppComponent {
colors: boolean[];
constructor() {
this.colors = [];
for (let i = 0; i < 10; i++) {
this.colors.push(false);
}
}
toggleTrue(id: number) {
this.colors[id] = !this.colors[id];
}
}
HTML
<div>
<div *ngFor="let color of colors; index as i">
<button
(click)="toggleTrue($event, i)"
id="{{i}}"
[ngClass]="{'active':colors[i]==true}"
>
button
</button>
</div>
</div>
CSS
.active {
color: red;
}
.inactive {
color: blue;
}
You could do some changes in your code. Firs in your HTML:
<button *ngFor="let color of colors; let i = index" (click) = "toggleTrue(i)" class = "coloredButton" [class.active]="color"> {{i}} < /button>
You noticed about [class.active]="color" this sentence verify if color is true the button will have the active class
.active {
border: 10px solid white;
}
I don't know how you generate colors array but I assumed somethings and also change your function in the class component
colors = new Array(10).fill(false);
toggleTrue(index: number) {
this.colors[index] = !this.colors[index]
}

Toggle Text between multiple buttons

I would like to have two buttons which are basically categories. Let's name them category A and category B. The are displayed left and right. Below i would like to display some text which is dependent of the chosen category (i.e the clicked button) so that category A shows text A and category B shows text B.
This if for html. I'm working on a wordpress homepage.
I was able to install one button which toggles text (basically button 1 = Category A). But i couldn't manage to insert a second button (basically button 2 = Category B). Any ideas? Highly appreciated!
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p>Click the button to swap the text of the DIV element:</p>
<p><button onclick="myFunction()">Click Me</button></p>
<div id="myDIV">Hello</div>
<script>
function myFunction() {
var x = document.getElementById("myDIV");
if (x.innerHTML === "Hello") {
x.innerHTML = "Swapped text!";
} else {
x.innerHTML = "Hello";
}
}
</script>
</body>
</html>
I expect to have 2 buttons which display 2 categories, the text should toggle according to which button has been clicked.
Could put the description in an attribute, then get the attributes value on click and change the html of the description. Here is a jsFiddle
<div>
<button class="js-button default-button" data-description="Category A's Description" onclick="myFunction(this)">
Category A
</button>
<button class="js-button default-button" data-description="Category B's Description" onclick="myFunction(this)">
Category B
</button>
</div>
<div id="js-description" class="description">
</div>
<script>
function myFunction(elem) {
var x = document.getElementById("js-description");
var description = elem.getAttribute('data-description');
x.innerHTML = description;
var button = document.getElementsByClassName('js-button');
for (var i = 0; i < button.length; i++) {
button[i].classList.remove('active-button');
}
elem.classList.add('active-button');
}
</script>
<style>
.default-button{
font-size:16px;
border-radius: 4px;
padding:7px 12px;
}
.active-button{
background:blue;
color:#fff;
}
.description{
margin-top:20px;
}
</style>
I don't really like all these solutions because everything is written from JS but contents probably come from database. So here is my solution :
// Native JS version
// Working Fiddle : https://jsfiddle.net/d34cbtw7/
var togglers = document.querySelectorAll('[data-toggle="tab"]');
for (var i = 0; i < togglers.length; i++) {
togglers[i].addEventListener('click', function() {
var tabs = document.querySelectorAll('.tab');
for(var j = 0; j < tabs.length; j++) {
tabs[j].classList.remove('active');
}
var $target = document.querySelector(this.getAttribute('data-target'));
$target.classList.add('active');
});
}
// jQuery version
$('body').on('click', '[data-toggle="tab"]', function(e) {
e.preventDefault();
// Select our target
var $target = $($(this).data('target'));
// Hide all tabs
$('.tab-contents .tab').removeClass('active');
// Show only $target tab
$target.addClass('active');
});
.tab-contents .tab {
display: none;
}
.tab-contents .tab.active {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button data-toggle="tab" data-target="#cat-A-content">
Cat A
</button>
<button data-toggle="tab" data-target="#cat-B-content">
Cat B
</button>
<div class="tab-contents">
<div class="tab active" id="cat-A-content">
My category A contents
</div>
<div class="tab" id="cat-B-content">
My category B contents
</div>
</div>
I also don't really like "onclick" attribute in HTML...
I've made a quick codepen as example.
You can achieve this by passing a parameter to the onClick function. In this example I keep track of the last button clicked, and the text it should render. If the last button clicked was the same button, the switched back to default. I hope this helps.
https://codepen.io/maffekill/pen/rbpjzw
HTML
<p>Click the button to swap the text of the DIV element:</p>
<p><button onclick="myFunction(1, 'TEXT A')">TEXT A</button></p>
<p><button onclick="myFunction(2, 'TEXT B')">TEXT B</button></p>
<div id="myDIV">Default Text</div>
JS
// Keep track of the button currently clicked
var activeBtn = null;
function myFunction(btnId, text) {
var x = document.getElementById("myDIV");
// If the last button is the same as the new one, show default text
if (activeBtn === btnId) {
x.innerHTML = "Default Text";
activeBtn = null
} else {
// Else show the text given to the text param
x.innerHTML = text;
activeBtn = btnId;
}
}
There are multiple ways to achieve this, but the easiest way I could come up with to explain this to you would be as following:
function myFunction(myEle) {
var x = document.getElementById("myDIV");
x.innerHTML = "This is category " + myEle.value;
}
<p>Click the button to swap the text of the DIV element:</p>
<p>
<button onclick="myFunction(this)" value="a">
Category A
</button>
<button onclick="myFunction(this)" value="b">
Category B
</button>
</p>
<div id="myDIV">Hello</div>
JSFiddle
No need to overcomplicate things.
Firstly you would like to send the clicked element from the caller (which in this case would be the clicked element as well, the <button> element). You could use JavaScript's thisfor this purpose.
Within your function you can name a parameter between parenthesis, so in my example above: function myFunction() contains a parameter called myEle so it will look like: function myFunction(myEle). Once the function will be triggered, the parameter called myEle will be set to the clicked element (or
JavaScript's this). You can simply access any of its attributes like value by using a dot: myEle.value.
Knowing the above, you could apply it to whatever you require your function to do (refer to my example code above).

How do I achieve arrow key navigation between divs

I have the following list of divs
<div id="multi-picker">
<div id="opt1">
<input type="checkbox"> Option 1
</div>
<div id="opt2">
<input type="checkbox"> Option 2
</div>
etc...
</div>
All working, but I'm not happy with the keyboard navigation. To navigate from opt1 to opt2 I need to press the tab key. Ideally I want to treat the options as a select and navigate with up/down arrow. Can this be done?
Alternatively...
Is there any way to have a multi-select with the options styled with checkboxes to reflect the selection state of each option?
I think you'll need javascript and using a plugin is an easier way to accomplish the task and maintain cross-browser functionality. However, here is a fiddle that, I think, kind of does what you want using just javascript. It defines additional attributes on your select elements and uses an onkeydown function to handle the navigation between the elements.
function keyPressed(e) {
var srcElement = e.target; // get the element that fired the onkeydown function
var dataset = false;
var selectList = false;
var next = "";
var prev = "";
if (srcElement.dataset) { // can we use HTML5 dataset?
dataset = true; // remember for later
// is this an element for which we care
if (srcElement.dataset.selectlist == 'true') {
selectList = true;
}
} else { // can't use HTML5 dataset, use getAttribute
if (srcElement.getAttribute('data-selectlist') == 'true') {
selectList = true;
}
}
// is it a select element and the user pressed either up arrow or down arrow
if (selectList && (e.keyCode == '38' || e.keyCode == '40')) {
// get the next and prev navigation options for this element
if (dataset) {
next = srcElement.dataset.next;
prev = srcElement.dataset.prev;
} else {
next = srcElement.getAttribute('data-next');
prev = srcElement.getAttribute('data-prev');
}
// up arrow was pressed and a prev element is defined
if (e.keyCode == '38' && prev != '') {
document.getElementById(prev).focus();
}
// down arrow was pressed and a next element is defined
if (e.keyCode == '40' && next != '') {
document.getElementById(next).focus();
}
// don't do native processing of the up or down arrow (page scrolling)
e.preventDefault;
}
}
document.onkeydown = keyPressed;
Here is the new html that contains the additional elements:
<div id="multi-picker">
<div id="opt1">
<input id="select1" type="checkbox" data-selectlist="true" data-prev="" data-next="select2"> Option 1
</div>
<div id="opt2">
<input id="select2" type="checkbox" data-selectlist="true" data-prev="select1" data-next=""> Option 2
</div>
</div>
This code is very specific to the problem presented and, though it may solve the problem, it would probably be better to use a general purpose plugin that would allow for more general application across your environment. You may also run into problems related to what your user expects the down and up arrow keys to do versus what you are doing by intercepting them.
In my experience I've run into problems where different browsers and even different end-user platforms present different behavior to the application making implementation consistency spotty. Many of the plugins are designed to eliminate that inconsistency and provide a cleaner, more intuitive interface.
Alternatively... Is there any way to have a multi-select with the options styled with checkboxes to reflect the selection state of each option?
That has multi-browser support? Not really. However, if you're a "It works in Chrome, it ships!" type dev, then you could totally fake a multi select to appear to have checkboxes that reflect the selection state of each option with good ol' CSS.
First our HTML:
<select multiple="multiple">
<option>Hello</option>
<option>World</option>
<option>Hello</option>
<option>World</option>
</select>
Next, dat CSS:
select {
border: none;
}
select:focus {
outline: none;
}
select option:before {
display: inline-block;
content: '';
width: 13px;
height: 13px;
position: relative;
margin-right: 3px;
background-image: url('http://i.imgur.com/5xszy3H.png');
background-position: -40px 0px;
top: 2px;
}
Using generated content, we've now applied our pseudo checkboxes. Let's add that last bit of CSS to give us a proper selected state!
select option:checked:before {
background-position: -40px -40px;
}
Woop.
Here's a fiddle to play around with: http://jsfiddle.net/9tLz6ugz/
In (limited) testing, this seemed to only work in Chrome and FireFox (both latest on OSX). Safari, IE, and Opera ignored the generated content all together. So, while not practical (at least not today), it's somewhat doable!
(Here's another fiddle without the background-image - requires a bit more work to get the look/feel consistent across browsers: http://jsfiddle.net/wpch98tg/ But does demonstrate use of unicode to fake a "checkbox" in a multi-select...)
I don't believe you'll find a way with plain HTML & CSS - this gets too far into customizing the core functionality so a bit of JavaScript should do the trick (I'm also using jQuery to make it slightly easier).
For my test, I used <label> instead of <div> to wrap the checkboxes, but this same idea holds true for divs. I just prefer labels so if JavaScript doesn't load, the checkboxes are still clickable.
<div class="fake-select">
<label for="fs1"><input type="checkbox" id="fs1" name="fs1" />Fake 1</label>
<label for="fs2"><input type="checkbox" id="fs2" name="fs2" />Fake 2</label>
[ ... ]
</div>
Basically, you need to track the user's keypresses and an "active" input. Look for up/down arrow key presses and change the active input based on that (scroll to it, add a class name for styling). I have it also allowing the space bar to check/uncheck the currently active box.
var fs = $(".fake-select"),
items = fs.find('input'),
divs = fs.find('label'),
active = 0;
// Hook keypresses
fs.keydown( function(e){
reCalculate(e);
rePosition();
return false;
});
// Click event for manual changes
items.unbind('click').click(function(e){
var thisInput = $(this),
thisLabel = $(this).parent();
window.active = thisLabel.index();
thisLabel.removeClass("checked");
if (thisInput.is(":checked"))
thisLabel.addClass("checked");
rePosition();
});
// Set new active element based on keypress
// Trigger click event if user hits space bar
function reCalculate(e){
var max = items.length -1,
cur = window.active,
charCode = (typeof e.which == "number" && e.which) ? e.which : e.keyCode;
if (charCode == 38) { // move up
cur--;
cur = (cur <= 0) ? 0 : cur;
}
if (charCode == 40) { // move down
cur++;
cur = (cur >= max) ? max : cur;
}
if (charCode == 32) { // space bar
items.eq(cur).focus().trigger('click');
}
window.active = cur;
}
// Add active class, call scroll function
function rePosition(){
divs.removeClass('active').eq(window.active).addClass('active');
scrollInView();
}
// Scroll the list so the active element is in view
function scrollInView(){
var target = divs.eq(window.active);
if (target.length) {
var top = fs.scrollTop() + target.position().top - fs.height()/2 + target.height()/2
fs.stop().animate({scrollTop: top}, 400);
}
return false;
}
jsFiddle example: http://jsfiddle.net/daCrosby/kqecngv0/13/
Can you use jQuery? This will activate the focus functionality via up/down arrow
function focusOnCheckboxes(locator){
var checkboxes = jQuery(locator).find(':checkbox');
var index = 0;
jQuery(locator).on('keydown', function(e){
if( e.keyCode == 38 ){
if(index > 0){
index = index - 1;
}
checkboxes[index].focus();
}
else if(e.keyCode == 40){
if(index < checkboxes.length - 1){
index = index + 1;
}
else{
index = checkboxes.length - 1;
}
checkboxes[index].focus();
}
else{
//console.log(e.keyCode);
}
});
}
// set the locator
focusOnCheckboxes('body');

knockoutjs css binding value when boolean computed property is false/true

I am binding my currently selected item like this:
The selected item gets visible, but how can I give the unselected items an unselected/default background color? When I set a background color to the template class I do not see anymore the background color set from the selection via mouse click.
Is the problem maybe that I have 2 backgrounds set but none is removed?
<div data-bind="foreach: items">
<div class="cellContainer" >
<div class="template" data-bind="click: $parent.selectItem, css: { selected: isSelected }">
<div data-bind="text: number"></div>
</div>
</div>
</div>
.selected {
background-color: #0094ff;
}
Sounds like a cascade issue. Make sure your ".template" style is defined before your ".selected" style in your css file.
Make sure your selectItem method resets isSelected on all elements before setting it to true on the argument. A naive implementation could be:
var ViewModel = function() {
// Skipped the rest for brevity...
self.selectItem = function(item) {
// Deselect all items first
for (var i = 0; i < self.items().length; i++) {
self.items()[i].isSelected(false);
}
// Select the argument
item.isSelected(true);
};
};
See this jsfiddle for a demo.
However, it is often easier to keep a reference on the parent view model to the currently selected item, and change the css binding to something like:
css: { selected: $parent.TheOneSelectedItem().number() == number() }
See this fiddle for the alternate demo.
Something like this might work.
.template {
background-color: #fff;
}
.template.selected {
background-color: #0094ff;
}
It does not look like a knockout issue.