Getting farthest (oldest) ancestor by class name with Prototype - prototypejs

I've wasted way too much time on this today. Let's assume I have something like this:
<div id="p1" class="hit">
<div id="p2" class="no-hit">
<div id="p3" class="hit">
<div id="p4" class="no-hit">
<div id="p5" class="hit">
el
</div></div></div></div></div>
So my element is being passed to a function, and if it's a descendant of a .hit element, I want el to equal the oldest ancestor with the hit class (in this case #p1).
In jQuery, it would be as simple as using el.parents(".hit") and pointing to the last one, but I'm working on legacy code and have to use prototype. The following code works on jsfiddle, but causes an illegal token error on production:
if(item.up('.hit') != undefined){
while(item.up('.hit') != undefined){
item = item.up('.hit');
}
}
Please prototype, tell me you have an easy way to accomplish this.

Prototype and jQuery are similar in this regard (at least, in my interpretation of the documentation). You should be able to use the .up method in Prototype as well.
The code you provided should work with Prototype as well. You can see an example here.
If not, you could always try the .previous method.

if(typeof item.up('.hit') != 'undefined') {
while(typeof item.up('.hit') != 'undefined') {
item = item.up('.hit');
}
}

Related

Binding Vue.js to all instances of an element, without(?) using Components

Today I'm learning Vue.js, and I have a few ideas of where it might be really useful in a new project that's an off-shoot of an existing, live project.
I like the idea of trying to replace some of my existing functionality with Vue, and I see that Components may be quite handy as quite a lot of functionality is re-used (e.g. Postcode lookups).
Once of the pieces of functionality I've used for an age is for invalid form elements - currently in jQuery when a form input or textarea is blurred I add a class of form__blurred, and that is coupled with some Sass such as:
.form__blurred {
&:not(:focus):invalid {
border-color:$danger;
}
}
This is to avoid styling all required inputs as errors immediately on page load.
I'm totally fine with doing this in jQuery, but I figured maybe it could be done in Vue.
I have an idea of how I might do it with components thanks to the laracasts series, but my form inputs are all rendered by Blade based on data received from Laravel and it doesn't seem like a neat solution to have some of the inputs rendered in Javascript, for a number of reasons (no JS, confusion about where to find input templates, etc).
I figured something like the following simplified example would be handy
<input type="text" class="form__text" v-on:blur="blurred" v-bind:class="{ form__blurred : isBlurred }" />
<script>
var form = new Vue({
el : '.form__input',
data : {
isBlurred : false
},
methods : {
blurred : function() {
this.isBlurred = true;
}
}
});
</script>
That actually works great but, as expected, it seems like using a class selector only selects the first instance, and even if it didn't, I'm guessing changing the properties would apply to all elements, not each one individually.
So the question is - is this possible with pre-rendered HTML, in a way that's smarter than just looping through a selector?
If it is, is there a way to create the Vue on a .form element and apply this function to both .form__input and .form__textarea?
Or, as is probably the case, is this just not a good use-case for Vue (since this is actually a lot more code than the jQuery solution).
Sounds like a great use case for a Custom Directive.
Vue allows you to register your own custom directives. Note that in Vue 2.0, the primary form of code reuse and abstraction is components - however there may be cases where you just need some low-level DOM access on plain elements, and this is where custom directives would still be useful.
<div id="app">
<input type="text" name="myforminput" v-my-directive>
</div>
<script>
Vue.directive('my-directive', {
bind: function (el) {
el.onblur = function () {
el.classList.add('form__blurred');
}
}
});
var app = new Vue({
el: '#app'
});
</script>
You can also add the directive locally to a parent component, if it makes sense for your application.

React 0.14.0-RC1 / React-Router 1.0.0-RC1 - Cannot read property 'props' of null

I am working off Alex Bank's "Building a Polling App with Socket IO
and React.js" (Lynda.com), but I am trying to upgrade it to
react-router 1.0.0-RC1.
My github repository can be found here ....
Problem:
When a speaker signs in and creates a presentation, a list of questions successful appears. However, when the speaker clicks on a respective question to emit to the attendees, I receive the error: "Cannot read property 'props' of null" which identifies the error in the Question.js component:
ask(question) {
console.log('this question: ' + JSON.stringify(question));
this.props.emit('ask', question); <--- Console points to this
}
But I do not believe that is the problem per se. I believe the actual problem is that this emit is not reaching the socket.on in the app.
APP.js:
componentWillMount() {
this.socket = io('http://localhost:3000');
this.socket.on('ask', this.ask.bind(this));
....
}
ask(question) {
sessionStorage.answer = '';
this.setState({ currentQuestion: question });
}
I believe it is react-router related, but the parent route does have component {APP}, and Speaker is a child route and the Speaker component does import the Question component, so I am assuming the Question component is connected to the APP's.
In Alex's project it is working, but he uses:
"react": "^0.13.3",
"react-router": "^0.13.3",
Any chance someone can offer me some insight on this?
Many Thanks!
If your error says "Cannot read property 'props' of null," that's exactly what's happening: you're attemping to call .props on a value that is null.
However, the real problem is in this code:
ask(question) {
console.log('this question: ' + JSON.stringify(question));
this.props.emit('ask', question);
}
addQuestion(question, index) {
return (
<div key={ index } className="col-xs-12 col-sm-6 col-md-3">
<span onClick={ this.ask.bind(null, question) }>{ question.q }</span>
</div>
);
}
Specifically, this code (which isn't included in the question):
onClick={ this.ask.bind(null, question) }>
You're assigning a click handler to a version of this.ask that's bound to null; this works fine with React.createClass-type components, because React forcibly and automatically binds all component methods to the component instance, no matter what you pass as the first argument to .bind() (and so null is commonly used; I think React actually yells at you otherwise). However, this is not the case with ES6 classes; you're literally setting this inside ask() to null.
A correct version would be
onClick={ this.ask.bind(this, question) }>
or, commonly, as an anonymous function
onClick={ () => this.ask(question) }>
The error message indicates this is null. The reason is react doesn't autobind this to the react element. That is, the this doesn't reference to the element itself while invoking ask(question) method. You only have to bind it in the constructor: this.ask = this.ask.bind(this). It's better for you to read the ES6 class notes on official react blog.
Reading react-router upgrade guide is also a good idea :)

Angular Validation $parsers

When we have a controller or ng-model-controller we can do
ctrl.$parsers.push(function(viewValue){
ctrl.$setValidity('valid', true);
});
and in the end $digest is called automatically and validation renders.
What if I want to validate a field on blur. and I do
element.blur(function(){
[validations]
ctrl.$setValidity('valid', false);
})
and the result don't change on html with elements ng-binded, how to render this change?
You have to call ctrl.$setValidity('valid', false); within scope.$apply
scope.$apply(function(){
ctrl.$setValidity('valid', false);
})
Or better yet, in order to avoid problems further on, use CSS to solve this problem. Let's assume the following code:
<textarea ng-model="description" ng-minlength="50"></textarea>
<span class="error" ng-show="myform.$error.minlength">Too short!</span>
<span class="error" ng-show="myform.$error.someOtherValidation">Err!</span>
When you start typing, Angular starts validation, and errors appear (which is sometimes desirable, but more often, it's not).
What you can do in order not to mess up Angular's internals, is some CSS magic:
textarea:focus ~ .error {
display:none;
}
The tilde is a general sibling selector, so as long as you have your fields focused all your errors will stay hidden.

knockout.js and Firefox Save Passwords on login form

Firefox populates a form with my username/password. This is using knockout.js to bind the input but it won't update the values on this kind of populating. Am I missing something say on a page load? When it populates and the user hits submits, the values are blank.
(function (app, $, undefined) {
app.viewModel = app.viewModel || {};
app.login = {};
app.viewModel.login = {
userName: ko.observable(''),
password: ko.observable(''),
returnUrl: ''
};
app.viewModel.login.submit = function () {
sso.login(app.viewModel.login.userName(), app.viewModel.login.password(), app.viewModel.login.returnUrl);
};
app.login.init = function (returnUrl) {
app.viewModel.login.returnUrl = returnUrl;
ko.applyBindings(app.viewModel);
};
})(window.app = window.app || {}, jQuery);
The way that I have dealt with this in the past is to use a wrapper to the value binding that initializes the value from the element's current value.
It would look like (this one is simplified to only work with observables):
ko.bindingHandlers.valueWithInit = {
init: function(element, valueAccessor, allBindingsAccessor, context) {
var observable = valueAccessor();
var value = element.value;
observable(value);
ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor, context);
},
update: ko.bindingHandlers.value.update
};
So, you would use valueWithInit instead of value. You just need to make sure that ko.applyBindings is not called before the autocomplete has been able to do its job.
http://jsfiddle.net/rniemeyer/TeFAX/
I found the solution here not really satisfying. Although the approach is rather interesting, it fails when the user is choosing the account later and the browser does allow to use the stored credentials (e.g. if there are more than one credentials stored). It failed as well when you started typing in the password and deleted to get back to the original password (in Firefox at least).
Additionally, I did not really like the timeout to give the browser time - just not that nice.
My solution:
which isn't really one, but I thought I share nonetheless
Simple update our model manually before doing the login in the submit callback.
Using jQuery, something like self.password($("#password").val()) should do it.
Alternatively, using the existing bindings, triggering a change event seems to work as well - e.g. $("#password").change().
The Pros:
is only for credential fields, so probably a one time thing for your site
is simple and clean - one or two lines at the proper place
seems to always work reliably, no matter what browser, credential setup or usage pattern
The Cons:
breaks again the nice separation Knockout.js provides
is not a solution but rather a workaround
I will stick with that for now because I found it just reliable working. It would be nice to tell Knockout to reevaluate the bindings directly rather than storing the value back manually or triggering it via the change event. But I haven't found anything so far.
Just thinking a bit ahead - the same problem should arise when the browser auto-completes any form (e.g. like an address) - which means means some sort of general function doing the above would be nice (probably calling the change trigger on each input field of the form)
Edit:
Some quick code demonstrating the idea
The HTML:
<form id="myForm" data-bind="submit: login">
Email: <input type="text" data-bind="value: email" /><br/>
Password: <input type="password" data-bind="value: password" /><br/>
<button type="submit">Login</button>
</form>
And the Javascript:
function ViewModel() {
var self = this;
self.email = ko.observable("");
self.password = ko.observable("");
self.login = function() {
$("#myForm").find("input").change();
//Now the observables contain the recent data
alert(ko.mapping.toJSON(self));
};
}

Validating HTML 5 with REL

I have browsed for a question similar to this but haven't happened to find exactly what i need but apologies if it has been answered somewhere.
I have started using java script light-boxes in my webpage to display images and am told to place on the links:
This means that the images now open in lightboxes however an HTML 5 validator says that 'lightbox' is obviously not an allowed link type.
How can i relate the required links to the lightbox java script so that it validates?
thanks alot in advance,
matt
Either
Ignore the validation errors (as they don't cause any problems), or
Change from rel="lightbox" to something like data-lightbox="true". Any attribute starting with "data-" is allowed and valid in HTML5.
Matthew's answer works, but you must remember to customize your lightbox source code as well. The example of such a modification you can see here: http://wasthere.com/lightbox2/js/custom-lightbox.js - it works (you can see here e.g. http://wasthere.com/asia/en/show-entry/141/kerala---munnar-(india) ), HTML5 validation passes. Check the comments in source file above, if you decide to use it - just change all "rel" attributes relevant to light box images to "data-rel" on your site.
Best regards,
Lukas
what helped me validating it, is based on what was stated above:
Javascript
function externalLinks()
{
if (!document.getElementsByTagName) return;
var anchors = document.getElementsByTagName("a");
for (var i=0; i<anchors.length; i++)
{
var anchor = anchors[i];
if (anchor.getAttribute("href") && anchor.getAttribute("rel") == "external")
{
anchor.target = "_blank";
}
if (anchor.getAttribute("href") && anchor.getAttribute("rel") == "lightbox")
{
anchor.setAttribute('data-lightbox','lightbox');
}
}
}
window.onload = externalLinks;
HTML:
<a href='assets/newsTemplate/07_350wtd.jpg' rel='lightbox' title='DumbThumb'><img src='assets/newsTemplate/07_350wtd.jpg' alt='dumb'></img></a>
A si lo solucione:
Modifico el html:
<a href="img/2.jpg" data-rel="lightbox" title="" >
Modifico el javascript:
jQuery(function($) {
$("a[data-rel^='lightbox']").slimbox({/* Put custom options here */}, null, function(el) {
return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
});
});
}

Resources