Conflict with use of rel attribute in mootools - dom

Looking for some insight into this problem.
I have dynamically generated links on a page that launch a lightbox ie they use a rel="lightbox[...]" I'm also putting a class on the hyperlink to make a tooltip work.
<a id="a_-1_6" class="Tips2" href="/media/63/forest_150.jpg" rel="lightbox[examples]" data-title="Tractor" data-desc="description..." data-rel="std" title="" style="opacity: 1; visibility: visible;">
And in the dom ready event
var Tips2 = new Tips($$('.Tips2'), {
initialize: function() { this.tip.fade('hide'); },
onShow: function(tip) { tip.fade('in'); },
onHide: function(tip) { tip.fade('out'); }
});
This all works fine except the tip uses the rel attribute to store data, i'm presuming as its a pre-html5 - so my question is would this mean I need to make my own version of the Tips class in mootools to work off the data.* attributes? I'd like to see I'm not barking up the wrong tree before I try that.
Thanks

Could you make another element inside the Ahref, like:
<a id="a_-1_6" href="/media/63/forest_150.jpg" rel="lightbox[examples]" data-title="Tractor" data-desc="description..." data-rel="std" title="" style="opacity: 1; visibility: visible;">
<span class="Tips2">blah</span>
</a>
This way, you can avoid the conflict.

Tips' documentation states that you can change which property is checked for the tip text. By default, it is rel or href, but you can change it when initializing the new Tip:
var Tips2 = new Tips($$('.Tips2'), {
initialize: function() { this.tip.fade('hide'); },
onShow: function(tip) { tip.fade('in'); },
onHide: function(tip) { tip.fade('out'); },
text: 'data-text' // Will now check the data-text property for tooltip text
});

Related

YUI - get node from 'this' keyword

How can I select YUI node with this keyword?
Example:
<a href='javascript:test(this);'>Click Me!</a>
<script>
function test(el){
YUI().use('transition', function (Y) {
var selectedElement = Y.one(el);
});
}
</script>
Thanks a lot for help.
In your code, "this" will refer to the Window object, not the link. A better way to do this in YUI is:
Click Me!
<script>
YUI().use('transition', function(Y) {
Y.one('#my-link').on('click', function(e) {
e.halt();
var selectedElement = e.target;
});
});
</script>
This avoid JavaScript embedded in the markup, which is considered best practice. Also, it wraps all your code in the YUI sandbox, which is the recommended way.

Class prefix as selector for each function

I am able to do this using an ID prefix as the selector, but I need to be able to do it with classes instead. It's an each function for opening up different modal windows on the same page. I need to avoid using ID names because I have some modal windows that will have multiple links on the same page, and when using IDs, only the first link will work.
So here's the function as it works with IDs:
$('div[id^=ssfamodal-help-]').each(function() {
var sfx = this.id,
mdl = $(this),
lnk = $('.link-' + sfx),
cls = $('.ssfamodal-close'),
con = $('.ssfamodal-content');
lnk.click(function(){
mdl.show();
});
cls.click(function(){
mdl.hide();
});
mdl.click(function() {
mdl.hide();
});
con.click(function() {
return false;
});
});
and I'm trying to change it to classes instead, like:
$('div[class^=ssfamodal-help-]').each(function() {
var sfx = this.attr('class'),
etc.
But I cannot get it to work without using IDs. Is it possible?
EDIT Fixed error with semi-colon at end of Vars, and updated Fiddle with the fix. Still not working though.
Here's a Fiddle
** UPDATE **
To be clearer, I need to be able to refer to the same modal more than once on the same page. E.g.:
MODAL 1
MODAL 2
MODAL 3
MODAL 4
LINK TO MODAL 1
LINK TO MODAL 2
LINK TO MODAL 3
LINK TO MODAL 4
OTHER STUFF
LINK TO MODAL 1
LINK TO MODAL 4
LINK TO MODAL 3
OTHER STUFF
LINK TO MODAL 2
ETC.
When using classes get rid of the ID habit :
className1, className2, className3 ... etc
simply use
className
HTML:
<div class="ssfamodal-help-base ssfamodal-backdrop">
<div id="help-content" class="ssfamodal-content">
<span class="ssfamodal-close">[x]</span>
Howdy
</div>
</div>
<div class="ssfamodal-help-base ssfamodal-backdrop">
<div id="help-content" class="ssfamodal-content">
<span class="ssfamodal-close">[x]</span>
Howdy Ho
</div>
</div>
<span class="link-ssfamodal-help-base">One</span>
<span class="link-ssfamodal-help-base">Two</span>
LIVE DEMO
var $btn = $('.link-ssfamodal-help-base'),
$mod = $('.ssfamodal-help-base'),
$X = $('.ssfamodal-close');
$btn.click(function(i) {
var i = $('[class^="link-"]').index(this); // all .link-** but get the index of this!
// Why that?! cause if you only do:
// var i = $('.link-ssfamodal-help-base').index();
// you'll get // 2
// cause that element, inside a parent is the 3rd element
// but retargeting it's index using $('className').index(this);
// you'll get the correct index for that class name!
$('.ssfamodal-help-base').eq(i).show() // Show the referenced element by .eq()
.siblings('.ssfamodal-help-base').hide(); // hide all other elements (with same class)
});
$X.click(function(){
$(this).closest('.ssfamodal-help-base').hide();
});
From the DOCS:
http://api.jquery.com/eq/
http://api.jquery.com/index/
http://api.jquery.com/closest/
Here I created a quite basic example on how you can create a jQuery plugin of your own to handle modals: http://jsbin.com/ulUPIje/1/edit
feel free to use and abuse.
The problem is that class attributes can consist of many classes, rather than IDs which only have one value. One solution, which isn't exactly clean, but seems to work is the following.
$('div').filter(function () {
var classes = $(this).attr('class').split(/\s+/);
for (var i = 0; i < classes.length; i++)
if (classes[i].indexOf('ssfamodal-help-') == 0)
return true;
return false;
}).each(function() {
// code
});
jsFiddle
Or, equivalently
$('div').filter(function () {
return $(this).attr('class').split(/\s+/).some(function (e) {
return e.indexOf('ssfamodal-help-') == 0;
});
}).each(function() {
// code
});
jsFiddle
If there is one-to-one relationship between the modal helps and the modal links which it appears there is...can simplfy needing to match class values by using indexing.
For this reason you don't need unique class names, rather they just overcomplicate things. Following assumes classes stay unique however
var $helps=$('div[id^=ssfamodal-help-]');
var $help_links=$('div[id^=link-ssfamodal-help-]');
$help_links.click(function(){
var linkIndex= $help_links.index(this);
$helps.hide().eq( linkIndex ).show();
});
/* not sure if this is what's wanted, but appeared original code had it*/
$helps.click(function(){
$(this).hide()
})
/* close buttons using traverse*/
$('.ssfamodal-close').click(function(){
$(this).closest('div[id^=ssfamodal-help-]' ).hide();
});
Also believe that this code is a little more readable than original apporach
DEMO
Can you try this,
$('div[class^=ssfamodal-help-]').each(function() {
var sfx = $(this).attr('class');
console.log(sfx);
/*console log:
ssfamodal-help-base ssfamodal-backdrop
ssfamodal-help-base2 ssfamodal-backdrop
*/
});
Demo: http://jsfiddle.net/xAssR/51/
why don't you write like
$('div.classname').each(function() {
// you can write your desired code here
var sfx = this.attr('class');
var aa= this.attr('id');
});
or
$('.classname').each(function() {
// you can write your desired code here
var sfx = this.attr('class');
var aa= this.attr('id');
});
where classname is the name of the class used for the div in html
Thanks.

Validate fields after user has left a field

With AngularJS, I can use ng-pristine or ng-dirty to detect if the user has entered the field. However, I want to do client-side validation only after the user has left the field area. This is because when a user enters a field like e-mail or phone, they will always get an error thrown until they've completed typing out their full e-mail, and this is not an optimal user experience.
Example
UPDATE:
Angular now ships with a custom blur event:
https://docs.angularjs.org/api/ng/directive/ngBlur
From version 1.3.0 this can easily be done with $touched, which is true after the user has left the field.
<p ng-show="form.field.$touched && form.field.$invalid">Error</p>
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Angular 1.3 now has ng-model-options, and you can set the option to { 'updateOn': 'blur'} for example, and you can even debounce, when the use is either typing too fast, or you want to save a few expensive DOM operations (like a model writing to multiple DOM places and you don't want a $digest cycle happening on every key down)
https://docs.angularjs.org/guide/forms#custom-triggers and https://docs.angularjs.org/guide/forms#non-immediate-debounced-model-updates
By default, any change to the content will trigger a model update and
form validation. You can override this behavior using the
ngModelOptions directive to bind only to specified list of events.
I.e. ng-model-options="{ updateOn: 'blur' }" will update and validate
only after the control loses focus. You can set several events using a
space delimited list. I.e. ng-model-options="{ updateOn: 'mousedown
blur' }"
And debounce
You can delay the model update/validation by using the debounce key
with the ngModelOptions directive. This delay will also apply to
parsers, validators and model flags like $dirty or $pristine.
I.e. ng-model-options="{ debounce: 500 }" will wait for half a second
since the last content change before triggering the model update and
form validation.
I solved this by expanding on what #jlmcdonald suggested. I created a directive that would automatically be applied to all input and select elements:
var blurFocusDirective = function () {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, elm, attr, ctrl) {
if (!ctrl) {
return;
}
elm.on('focus', function () {
elm.addClass('has-focus');
scope.$apply(function () {
ctrl.hasFocus = true;
});
});
elm.on('blur', function () {
elm.removeClass('has-focus');
elm.addClass('has-visited');
scope.$apply(function () {
ctrl.hasFocus = false;
ctrl.hasVisited = true;
});
});
elm.closest('form').on('submit', function () {
elm.addClass('has-visited');
scope.$apply(function () {
ctrl.hasFocus = false;
ctrl.hasVisited = true;
});
});
}
};
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);
This will add has-focus and has-visited classes to various elements as the user focuses/visits the elements. You can then add these classes to your CSS rules to show validation errors:
input.has-visited.ng-invalid:not(.has-focus) {
background-color: #ffeeee;
}
This works well in that elements still get $invalid properties etc, but the CSS can be used to give the user a better experience.
I managed to do this with a pretty simple bit of CSS. This does require that the error messages be siblings of the input they relate to, and that they have a class of error.
:focus ~ .error {
display:none;
}
After meeting those two requirements, this will hide any error message related to a focused input field, something that I think angularjs should be doing anyway. Seems like an oversight.
This seems to be implemented as standard in newer versions of angular.
The classes ng-untouched and ng-touched are set respectively before and after the user has had focus on an validated element.
CSS
input.ng-touched.ng-invalid {
border-color: red;
}
Regarding #lambinator's solution... I was getting the following error in angular.js 1.2.4:
Error: [$rootScope:inprog] $digest already in progress
I'm not sure if I did something wrong or if this is a change in Angular, but removing the scope.$apply statements resolved the problem and the classes/states are still getting updated.
If you are also seeing this error, give the following a try:
var blurFocusDirective = function () {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, elm, attr, ctrl) {
if (!ctrl) {
return;
}
elm.on('focus', function () {
elm.addClass('has-focus');
ctrl.$hasFocus = true;
});
elm.on('blur', function () {
elm.removeClass('has-focus');
elm.addClass('has-visited');
ctrl.$hasFocus = false;
ctrl.$hasVisited = true;
});
elm.closest('form').on('submit', function () {
elm.addClass('has-visited');
scope.$apply(function () {
ctrl.hasFocus = false;
ctrl.hasVisited = true;
});
});
}
};
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);
It might work for you to write a custom directive that wraps the javascript blur() method (and runs a validation function when triggered); there's an Angular issue that has a sample one (as well as a generic directive that can bind to other events not natively supported by Angular):
https://github.com/angular/angular.js/issues/1277
If you don't want to go that route, your other option would be to set up $watch on the field, again triggering validation when the field is filled out.
To pick up further on the given answers, you can simplify input tagging by using CSS3 pseudo-classes and only marking visited fields with a class to delay displaying validation errors until the user lost focus on the field:
(Example requires jQuery)
JavaScript
module = angular.module('app.directives', []);
module.directive('lateValidateForm', function () {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
$inputs = element.find('input, select, textarea');
$inputs.on('blur', function () {
$(this).addClass('has-visited');
});
element.on('submit', function () {
$inputs.addClass('has-visited');
});
}
};
});
CSS
input.has-visited:not(:focus):required:invalid,
textarea.has-visited:not(:focus):required:invalid,
select.has-visited:not(:focus):required:invalid {
color: #b94a48;
border-color: #ee5f5b;
}
HTML
<form late-validate-form name="userForm">
<input type="email" name="email" required />
</form>
based on #nicolas answer.. Pure CSS should the trick, it will only show the error message on blur
<input type="email" id="input-email" required
placeholder="Email address" class="form-control" name="email"
ng-model="userData.email">
<p ng-show="form.email.$error.email" class="bg-danger">This is not a valid email.</p>
CSS
.ng-invalid:focus ~ .bg-danger {
display:none;
}
Here is an example using ng-messages (available in angular 1.3) and a custom directive.
Validation message is displayed on blur for the first time user leaves the input field, but when he corrects the value, validation message is removed immediately (not on blur anymore).
JavaScript
myApp.directive("validateOnBlur", [function() {
var ddo = {
restrict: "A",
require: "ngModel",
scope: {},
link: function(scope, element, attrs, modelCtrl) {
element.on('blur', function () {
modelCtrl.$showValidationMessage = modelCtrl.$dirty;
scope.$apply();
});
}
};
return ddo;
}]);
HTML
<form name="person">
<input type="text" ng-model="item.firstName" name="firstName"
ng-minlength="3" ng-maxlength="20" validate-on-blur required />
<div ng-show="person.firstName.$showValidationMessage" ng-messages="person.firstName.$error">
<span ng-message="required">name is required</span>
<span ng-message="minlength">name is too short</span>
<span ng-message="maxlength">name is too long</span>
</div>
</form>
PS. Don't forget to download and include ngMessages in your module:
var myApp = angular.module('myApp', ['ngMessages']);
ng-model-options in AngularJS 1.3 (beta as of this writing) is documented to support {updateOn: 'blur'}. For earlier versions, something like the following worked for me:
myApp.directive('myForm', function() {
return {
require: 'form',
link: function(scope, element, attrs, formController) {
scope.validate = function(name) {
formController[name].isInvalid
= formController[name].$invalid;
};
}
};
});
With a template like this:
<form name="myForm" novalidate="novalidate" data-my-form="">
<input type="email" name="eMail" required="required" ng-blur="validate('eMail')" />
<span ng-show="myForm.eMail.isInvalid">Please enter a valid e-mail address.</span>
<button type="submit">Submit Form</button>
</form>
Use field state $touched The field has been touched for this as shown in below example.
<div ng-show="formName.firstName.$touched && formName.firstName.$error.required">
You must enter a value
</div>
You can dynamically set the has-error css class (assuming you're using bootstrap) using ng-class and a property on the scope of the associated controller:
plunkr: http://plnkr.co/edit/HYDlaTNThZE02VqXrUCH?p=info
HTML:
<div ng-class="{'has-error': badEmailAddress}">
<input type="email" class="form-control" id="email" name="email"
ng-model="email"
ng-blur="emailBlurred(email.$valid)">
</div>
Controller:
$scope.badEmailAddress = false;
$scope.emailBlurred = function (isValid) {
$scope.badEmailAddress = !isValid;
};
If you use bootstrap 3 and lesscss you can enable on blur validation with the following less snippet:
:focus ~ .form-control-feedback.glyphicon-ok {
display:none;
}
:focus ~ .form-control-feedback.glyphicon-remove {
display:none;
}
.has-feedback > :focus {
& {
.form-control-focus();
}
}
outI used a directive. Here is the code:
app.directive('onBlurVal', function () {
return {
restrict: 'A',
link: function (scope, element, attrs, controller) {
element.on('focus', function () {
element.next().removeClass('has-visited');
element.next().addClass('has-focus');
});
element.on('blur', function () {
element.next().removeClass('has-focus');
element.next().addClass('has-visited');
});
}
}
})
All my input control has a span element as the next element, which is where my validation message is displayed and so the directive as an attribute is added to each input control.
I also have (optional).has-focus and has-visited css class in my css file which you see being referenced in the directive.
NOTE: remember to add 'on-blur-val' exactly this way to your input control without the apostrophes
By using ng-focus you can achieve your goal. you need to provide ng-focus in your input field. And while writing your ng-show derivatives you have to write a logic not equal too. Like the below code:
<input type="text" class="form-control" name="inputPhone" ng-model="demo.phoneNumber" required ng-focus>
<div ng-show="demoForm.inputPhone.$dirty && demoForm.inputPhone.$invalid && !demoForm.inputPhone.$focused"></div>
We can use onfocus and onblur functions. Would be simple and best.
<body ng-app="formExample">
<div ng-controller="ExampleController">
<form novalidate class="css-form">
Name: <input type="text" ng-model="user.name" ng-focus="onFocusName='focusOn'" ng-blur="onFocusName=''" ng-class="onFocusName" required /><br />
E-mail: <input type="email" ng-model="user.email" ng-focus="onFocusEmail='focusOn'" ng-blur="onFocusEmail=''" ng-class="onFocusEmail" required /><br />
</form>
</div>
<style type="text/css">
.css-form input.ng-invalid.ng-touched {
border: 1px solid #FF0000;
background:#FF0000;
}
.css-form input.focusOn.ng-invalid {
border: 1px solid #000000;
background:#FFFFFF;
}
</style>
Try here:
http://plnkr.co/edit/NKCmyru3knQiShFZ96tp?p=preview

Using KoGrid in HotTowel template

I am trying to use KoGrid in a HTML view within the HotTowel SPA template. I created a simple view:
<section>
<h2 class="page-title" data-bind="text: title"></h2>
<div class="gridStyle" data-bind="koGrid: gridOptions"></div>
</section>
and added the model data in the JS:
define(['services/logger'], function (logger) {
var vm = {
activate: activate,
title: 'My Grid'
};
return vm;
//#region Internal Methods
function activate() {
var self = this;
this.myData = ko.observableArray([{ name: "Moroni", age: 50 },
{ name: "Tiancum", age: 43 },
{ name: "Jacob", age: 27 },
{ name: "Nephi", age: 29 },
{ name: "Enos", age: 34 }]);
this.gridOptions = { data: self.myData };
return true;
}
//#endregion
});
The grid is on the page, but the styling seems to be rendering widths and positions completely wrong so that columns are on top of each other and most data is not visibly correct. The KoGrid.css file is being referenced correctly.
Thanks for any help.
The core of the problem is that "when KOGrid does its binding in Durandal/HotTowel, the KOGrid element is not yet part of the DOM". You need to ensure that KOGrid does not do its binding until after the view is attached. This can be achieved as follows:
1) Add a new observable to the viewmodel to hold a boolean value for whether the view has been attached or not by durandal:
isAttachedToView = ko.observable(false)
and expose it
isAttachedToView: isAttachedToView
2) Up date it to be true when the viewAttached function callback is invoked:
function viewAttached() {
isAttachedToView(true);
return true;
}
3) Surround your HTML with a ko if statement to ensure that bit of HTML is not evaluated (i.e. kogrid does not do its binding) until after the view is attached:
<!-- ko if: isAttachedToView() -->
<div data-bind="koGrid: { data: ...
<!-- /ko -->
4) Reset isAttachedToView to be false on deactivating view
function deactivate() {
isAttachedToView(false);
}
And expose this:
deactivate: deactivate
You have probably already figured this one out but was also faced with the same problem today. A quick look at the chrome inspector told me that koGrid dimensional properties have not registered correctly and this tells me its a timing issue. I found an answered question relating to the same problem here.
I did try this solution but there is still some work to do to make it play ball nicely. I have decided to give koGrid a miss since I don't really want all it's functionality anywayz :)

Does codemirror provide Cut, Copy and Paste API?

From http://codemirror.net/doc/manual.html, I only find getRange(),
undo(), redo() etc, and I can't find cut(), copy() and paste API,
and more when I try to run editor.execCommand("cut"), I get the error.
Could you help me? Thanks!
Using clipboard.js, you can define the text() function to grab the value of the CodeMirror's inner document.
Store a reference to the (<textarea>) editor's selector for convenience.
var editorSelector = '#editor' // or '#editor + .CodeMirror';
Instantiate a new ClipBoard object with reference to your button.
new Clipboard('.clip-btn-native', {
text: function(trigger) {
return getCodeMirrorNative(editorSelector).getDoc().getValue();
}
});
Retrieve a CodeMirror Instance via native JavaScript.
function getCodeMirrorNative(target) {
var _target = target;
if (typeof _target === 'string') {
_target = document.querySelector(_target);
}
if (_target === null || !_target.tagName === undefined) {
throw new Error('Element does not reference a CodeMirror instance.');
}
if (_target.className.indexOf('CodeMirror') > -1) {
return _target.CodeMirror;
}
if (_target.tagName === 'TEXTAREA') {
return _target.nextSibling.CodeMirror;
}
return null;
};
Demo
Please see complete; in-depth demo over at JSFiddle.
There are no CodeMirror APIs for cut/copy/paste because browser security restrictions forbid JavaScript from accessing the clipboard programmatically. Paste could be used to steal private data and Cut/Copy can be used as a more elaborate attack vector.
The browser's own native code handles user gestures that access the clipboard (keyboard shortcuts and context menu items), based solely on the currently selected text or focused text field.
This SO thread has a good summary of attempts to work around these restrictions. CodeMirror's approach is the first bullet: it uses a hidden textarea to ensure that user clipboard gestures work, but that still doesn't support programmatic APIs.
But there is a partial workaround: use a small Flash widget (this is the 2nd bullet in the thread above). Flash relaxes the restrictions on Copy/Cut (but not Paste) a bit. It still has to be triggered by some user event, but it could be something like clicking a button in your HTML UI. Wrappers like ZeroClipboard and Clippy make it simple to access to these capabilities without needing to know Flash. You'd need to write a little glue code to pull the appropriate string from CodeMirror when copying, but it shouldn't be too bad.
Add a hidden contenteditable div to your textarea editor wrapper. Contenteditable divs respect new lines and tabs, which we need when copying code.
Here is my CodePen demo
var content = $('.content');
var toCopy = content.find('.copy-this');
// initialize the editor
var editorOptions = {
autoRefresh: true,
firstLineNumber: 1,
lineNumbers: true,
smartIndent: true,
lineWrapping: true,
indentWithTabs: true,
refresh: true,
mode: 'javascript'
};
var editor = CodeMirror.fromTextArea(content.find(".editor")[0], editorOptions);
content[0].editor = editor;
editor.save();
// set editors value from the textarea
var text = content.find('.editor').text();
editor.setValue(text);
// setting with editor.getValue() so that it respects \n and \t
toCopy.text(editor.getValue());
$(document).on('click', '.copy-code', function() {
var content = $(this).closest('.content');
var editor = content[0].editor;
var toCopy = content.find('.copy-this')[0];
var innerText = toCopy.innerText // using innerText here because it preserves newlines
// write the text to the clipboard
navigator.clipboard.writeText(innerText);
});
.content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.CodeMirror {
height: fit-content !important;
}
.copy-code {
background: #339af0;
width: fit-content;
cursor: pointer;
}
<!-- resources -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.35.0/codemirror.css" />
<script src="https://codemirror.net/lib/codemirror.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.40.0/mode/javascript/javascript.min.js"></script>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="content">
<!-- button to copy the editor -->
<div class="copy-code" title="copy code">copy</div>
<!-- add contenteditable div as it respects new lines when copying unlike textarea -->
<div class="copy-this" contenteditable style="display: none"></div>
<textarea class="editor" style="display: none;">// here is a comment
// here is another comment
</textarea>
</div>