How do I achieve arrow key navigation between divs - html

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');

Related

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

Ng-class - Active / Inactive button

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

Disable a div in AngularJS with ng-click property?

I need to disable a div in AngularJS depend on a condition. Yes I can do it by using css change (giving low contrast and less color appearance) in appearance but I have ng-click property in it. So I need to prevent that calling also. How can I do it?
Here is my code:
<div ng-repeat="item in list.items" ng-click="goToFunction()" ng-disabled="true">
Yes ng-disabled does not work for div. It will for each html element which support the disable property (eg : input).
https://docs.angularjs.org/api/ng/directive/ngDisabled
I created a simple fiddle to demonstrate how to achieve it with a simple div http://jsfiddle.net/5Qc5k/1/
function MyCtrl($scope, $templateCache) {
$scope.isDisabled = false;
$scope.alertMe = function(){
if($scope.isDisabled === true){
return;
}
// disable the function
$scope.isDisabled = true;
alert("foo");
}
}
You can do that in controller ie:
<div ng-repeat="item in list.items" ng-click="goToFunction(item)" ng-disabled="true">
and after that in controller:
function goToFunction(item){
if(item.condition == disable){
return false
}
else {
// do something
}
}
To prevent the ng-click functionality based on disable you can do in following:-
<div ng-repeat="item in list.items" ng-disabled="item.condition" ng-click="goToFunction(item)">
item.value
</div>
$scope.goToFunction = function(item){
if(item.condition == false){
// Dont do anything
}else{
//do something
}
In this scenario when div is disabled, click functionality wont do anything, otherwise it will do.

Required Attribute Not work in Safari Browser

I have tried following code for make the required field to notify the required field but its not working in safari browser.
Code:
<form action="" method="POST">
<input required />Your name:
<br />
<input type="submit" />
</form>
Above the code work in firefox. http://jsfiddle.net/X8UXQ/179/
Can you let me know the javascript code or any workarround? am new in javascript
Thanks
Safari, up to version 10.1 from Mar 26, 2017, doesn't support this attribute, you need to use JavaScript.
This page contains a hacky solution, that should add the desired functionality: http://www.html5rocks.com/en/tutorials/forms/constraintvalidation/#toc-safari
HTML:
<form action="" method="post" id="formID">
<label>Your name: <input required></label><br>
<label>Your age: <input required></label><br>
<input type="submit">
</form>
JavaScript:
var form = document.getElementById('formID'); // form has to have ID: <form id="formID">
form.noValidate = true;
form.addEventListener('submit', function(event) { // listen for form submitting
if (!event.target.checkValidity()) {
event.preventDefault(); // dismiss the default functionality
alert('Please, fill the form'); // error message
}
}, false);
You can replace the alert with some kind of less ugly warning, like show a DIV with error message:
document.getElementById('errorMessageDiv').classList.remove("hidden");
and in CSS:
.hidden {
display: none;
}
and in HTML:
<div id="errorMessageDiv" class="hidden">Please, fill the form.</div>
The only drawback to this approach is it doesn't handle the exact input that needs to be filled. It would require a loop accross all inputs in the form and checking the value (and better, check for "required" attribute presence).
The loop may look like this:
var elems = form.querySelectorAll("input,textarea,select");
for (var i = 0; i < elems.length; i++) {
if (elems[i].required && elems[i].value.length === 0) {
alert('Please, fill the form'); // error message
break; // show error message only once
}
}
If you go with jQuery then below code is much better. Just put this code bottom of the jquery.min.js file and it works for each and every form.
Just put this code on your common .js file and embed after this file jquery.js or jquery.min.js
$("form").submit(function(e) {
var ref = $(this).find("[required]");
$(ref).each(function(){
if ( $(this).val() == '' )
{
alert("Required field should not be blank.");
$(this).focus();
e.preventDefault();
return false;
}
}); return true;
});
This code work with those browser which does not support required (html5) attribute
Have a nice coding day friends.
I had the same problem with Safari and I can only beg you all to take a look at Webshim!
I found the solutions for this question and for this one very very useful, but if you want to "simulate" the native HTML5 input validation for Safari, Webshim saves you a lot of time.
Webshim delivers some "upgrades" for Safari and helps it to handle things like the HMTL5 datepicker or the form validation. It's not just easy to implement but also looks good enough to just use it right away.
Also useful answer on SO for initial set up for webshim here! Copy of the linked post:
At this time, Safari doesn't support the "required" input attribute. http://caniuse.com/#search=required
To use the 'required' attribute on Safari, You can use 'webshim'
1 - Download webshim
2 - Put this code :
<head>
<script src="js/jquery.js"></script>
<script src="js-webshim/minified/polyfiller.js"></script>
<script>
webshim.activeLang('en');
webshims.polyfill('forms');
webshims.cfg.no$Switch = true;
</script>
</head>
I have built a solution on top of #Roni 's one.
It seems Webshim is deprecating as it won't be compatible with jquery 3.0.
It is important to understand that Safari does validate the required attribute. The difference is what it does with it. Instead of blocking the submission and show up an error message tooltip next to the input, it simply let the form flow continues.
That being said, the checkValidity() is implemented in Safari and does returns us false if a required filed is not fulfilled.
So, in order to "fix it" and also show an error message with minimal intervention (no extra Div's for holding error messages) and no extra library (except jQuery, but I am sure it can be done in plain javascript)., I got this little hack using the placeholder to show standard error messages.
$("form").submit(function(e) {
if (!e.target.checkValidity()) {
console.log("I am Safari"); // Safari continues with form regardless of checkValidity being false
e.preventDefault(); // dismiss the default functionality
$('#yourFormId :input:visible[required="required"]').each(function () {
if (!this.validity.valid) {
$(this).focus();
$(this).attr("placeholder", this.validationMessage).addClass('placeholderError');
$(this).val(''); // clear value so it shows error message on Placeholder.
return false;
}
});
return; // its invalid, don't continue with submission
}
e.preventDefault(); // have to add it again as Chrome, Firefox will never see above
}
I found a great blog entry with a solution to this problem. It solves it in a way that I am more comfortable with and gives a better user experience than the other suggestions here. It will change the background color of the fields to denote if the input is valid or not.
CSS:
/* .invalid class prevents CSS from automatically applying */
.invalid input:required:invalid {
background: #BE4C54;
}
.invalid textarea:required:invalid {
background: #BE4C54;
}
.invalid select:required:invalid {
background: #BE4C54;
}
/* Mark valid inputs during .invalid state */
.invalid input:required:valid {
background: #17D654 ;
}
.invalid textarea:required:valid {
background: #17D654 ;
}
.invalid select:required:valid {
background: #17D654 ;
}
JS:
$(function () {
if (hasHtml5Validation()) {
$('.validate-form').submit(function (e) {
if (!this.checkValidity()) {
// Prevent default stops form from firing
e.preventDefault();
$(this).addClass('invalid');
$('#status').html('invalid');
}
else {
$(this).removeClass('invalid');
$('#status').html('submitted');
}
});
}
});
function hasHtml5Validation () {
return typeof document.createElement('input').checkValidity === 'function';
}
Credit: http://blueashes.com/2013/web-development/html5-form-validation-fallback/
(Note: I did extend the CSS from the post to cover textarea and select fields)
I use this solution and works fine
$('#idForm').click(function(e) {
e.preventDefault();
var sendModalForm = true;
$('[required]').each(function() {
if ($(this).val() == '') {
sendModalForm = false;
alert("Required field should not be blank."); // or $('.error-message').show();
}
});
if (sendModalForm) {
$('#idForm').submit();
}
});
The new Safari 10.1 released Mar 26, 2017, now supports the "required" attribute.
http://caniuse.com/#search=required
You can add this event handler to your form:
// Chrome and Firefox will not submit invalid forms
// so this code is for other browsers only (e.g. Safari).
form.addEventListener('submit', function(event) {
if (!event.target.checkValidity()) {
event.preventDefault();
var inputFields = form.querySelectorAll('input');
for (i=0; i < inputFields.length; i++) {
if (!inputFields[i].validity.valid) {
inputFields[i].focus(); // set cursor to first invalid input field
return false;
}
}
}
}, false);
Within each() function I found all DOM element of text input in the old version of PC Safari, I think this code useful for newer versions on MAC using inputobj['prpertyname'] object to get all properties and values:
$('form').find("[required]").each(function(index, inputobj) {
if (inputobj['required'] == true) { // check all required fields within the form
currentValue = $(this).val();
if (currentValue.length == 0) {
// $.each((inputobj), function(input, obj) { alert(input + ' - ' + obj); }); // uncomment this row to alert names and values of DOM object
var currentName = inputobj['placeholder']; // use for alerts
return false // here is an empty input
}
}
});
function customValidate(){
var flag=true;
var fields = $('#frm-add').find('[required]'); //get required field by form_ID
for (var i=0; i< fields.length;i++){
debugger
if ($(fields[i]).val()==''){
flag = false;
$(fields[i]).focus();
}
}
return flag;
}
if (customValidate()){
// do yor work
}

Having a permanent value in an input field while still being able to add text to it

I don't know if this is possible but I would like to have an input field where I would have a value that is not editable by the user.
However, I don't want the input field to be "readonly" because I still want the user to be able to add text after the value.
If you have any idea on how to do this, let me know please that would help me a lot.
EDIT: I use html forms.
You can position the text on top of the input field to make it look as if it is inside it. Something like this:
<input type="text" name="year" style="width:3.5em;padding-left:1.5em;font:inherit"><span style="margin-left:-3em;margin-right:10em;">19</span>
This way your input field will start with "19" which can not be edited, and the user can add information behind this.
Basically what you do is set the input field to a fixed width, so that you know how much negative margin-left to give the span with your text in it in order for it to be positioned exactly at the start of the input field.
You might need to fiddle with the margin-left of the span depending on the rest of your css.
Then also adding pedding-left to the input field, to make sure the user starts typing after your text and not under it.
font:inherit should make sure both your text and the text typed by the user are in the same font.
And if you want to put anything to the right of this input field, do add margin-right to the span with your text, as otherwise other content might start running over your input field as well.
seems a little weird to me ..why not just use a text output and afterwards the input field?
like sometimes used for the birthdate (although, maybe not anymore..)
birthyear: 19[input field]
edit:
with some javascript stuff you could realise something like that you asked for, though
an input field with text and catching keystrokes within that field while only allowing some after what you want to be always there - but, well, you would need to use js ..and if its just for that, Id rather say its not necessary
edit:
if you want to use a trick just for the viewer you could use a background-image/border-style that surrounds a text and the input field, thus making it look like text and input are the same input-box.
Sounds like you want placeholder text. In HTML5 you can set the placeholder attribute on any input element. This will work in modern browsers.
<input type="email" placeholder="jappleseed#appletree.com" name="reg_email" />
Now, for older browsers this won't work. You'll need a JavaScript alternative to provide the same UI value.
This can work for all browsers:
<input type="text" value="Search" onfocus="if (this.value == 'Search') {this.value = '';}" onblur="if (this.value == '') {this.value = 'Search';}">
but it's not recommended because there is a better way (really, it's a combination of the first two approaches): Use HTML5 markup for new browsers; jQuery and modernizr for old browsers. This way you can have only one set of code that will support all user cases.
Taken directly from webdesignerwall.com:
<script src="jquery.js"></script>
<script src="modernizr.js"></script>
<script>
$(document).ready(function(){
if(!Modernizr.input.placeholder){
$('[placeholder]').focus(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
input.removeClass('placeholder');
}
}).blur(function() {
var input = $(this);
if (input.val() == '' || input.val() == input.attr('placeholder')) {
input.addClass('placeholder');
input.val(input.attr('placeholder'));
}
}).blur();
$('[placeholder]').parents('form').submit(function() {
$(this).find('[placeholder]').each(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
}
})
});
}
</script>
[You'll need both jquery.js and modernizr.js installed in the same folder as your webpage.]
Note: I have a feeling that a little more research might reveal that modernizr isn't needed for this at all, though I could be wrong about that particular point.
Perhaps, then, you want a select menu?
<select name="mySelectMenu">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
Sorry if this isn't what you want either. I'm grasping at straws because what you are asking for is very vague. Maybe you should give an example of what one of these 'editable but not editable' inputs would be used for.
Also, you could use a select and a text input.
The main problem is to determine the position of the cursor. This can be done e.g. using the following function:
function getCaret(el) {
var pos = -1;
if (el.selectionStart) {
pos = el.selectionStart;
}
else if (document.selection) {
el.focus();
var r = document.selection.createRange();
if (r != null) {
var re = el.createTextRange();
var rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
pos = rc.text.length;
}
}
return pos;
}
Now you can install an event handler for the key press and check whether the pressed key was inside the immutable part of the value of the textarea. If it was there the event handler returns false, otherwise true. This behavior can be wrapped into a simple object:
function Input(id, immutableText) {
this.el = document.getElementById(id);
this.el.value = immutableText;
this.immutableText = immutableText;
this.el.onkeypress = keyPress(this);
}
function keyPress(el) {
return function() {
var self = el;
return getCaret(self.el) >= self.immutableText.length;
}
}
Input.prototype.getUserText = function() {
return this.el.value.substring(this.immutableText.length);
};
var input = new Input("ta", "Enter your name: ");
var userText = input.getUserText();
You can check it on jsFiddle (use Firefox or Chrome).
I came up with this:
```
if (e.target.value == '' || e.target.value.length <= 3) {
e.target.value = '+91-';
}
```