I'm starting to look into the whole world of RJS and Prototype/jQuery in Rails and am a little bit confused. There seems to be no clear line of where to use one or the other.
Say I wanted one of the "Active, Hot, Week" tabs like the ones here on SO. When pressing one of them, I want to remove a CSS class (like "active-tab") from the one I was on and add it to the one I clicked on. Then I also want to reload the div containing the items and put in the new items into it.
Seems the class-changing thing would be easiest to do in pure javascript, say put the code in application.js and then updating the div with the content would obviously be easiest in RJS. But what should one do?
If you're comfortable with writing JavaScript then by all means use JavaScript. There's nothing wrong with that; you don't have to use RJS just because it exists. In fact you'll probably find that its abstractions get in the way.
However, if you'd rather write Ruby code that generates JavaScript, just as you write Ruby code that generates SQL in ActiveRecord Migrations, then RJS is the right tool for the job. Or you can use both: RJS for fairly simple things and then drop down to JavaScript for more complex behaviour. Use what feels right to you.
If you want to give users that ability to link to the generated page directly, then definitly go for a static page. Using AJAX breaks the back button unless you use something like Really Simple History (which is not 100% cross browser), so going the JS route with your page navigation will almost certainly cause some of your users problems.
That said, what you have discussed already would be fine, I think - just have a class change in your RJS file, then you might even find it useful to update the div contents using page.replace and a partial:
page.replace(dom_id, :partial => #page_content);
Related
I have some simple JS ERB files that work perfectly the first time around. However, the issue is that when the trigger for the JS is pressed multiple times, it doesn't work client-side (even though it works server-side) - which I suspect is related to Turbolinks 5.
One such example of my JS.erb is this:
$("#a-<%= #answer.id %>-accept-answer i.accepted-answer-checkbox.fa.fa-check-square-o").addClass('fa-check-square voted');
$("#a-<%= #answer.id %>-accept-answer i.accepted-answer-checkbox.fa.fa-check-square-o").removeClass('fa-check-square-o');
That's in my Questions controller & /questions/accept_answer.js.erb.
But, right now, my questions.coffee file is blank.
What's the best way to tackle this?
Honestly, CoffeeScript has Ruby-style interpolation, so the easiest way to convert those would be to just go and do something like
$("#a-#{#answer.id}-accept-answer i.accepted-answer-checkbox.fa.fa-check-square-o").addClass 'fa-check-square voted'
$("#a-#{#answer.id}-accept-answer i.accepted-answer-checkbox.fa.fa-check-square-o").removeClass 'fa-check-square-o'
But what you're trying to do, would probably be better served by toggling those three classes, having them in an initial state that opposes each other, so something like:
$("#a-#{#answer.id}-accept-answer i.accepted-answer-checkbox.fa").click (el) ->
$.each ['fa-check-square', 'voted', 'fa-check-square-o'], (_, val) ->
$(el).find('i.accepted-answer-checkbox.fa').toggleClass val
Of course, I could be making it too complex, but this seems to solve the problem of toggling the classes that are desired.
newbie question.
I've read some of the W3Schools, I also read a lot from other sources on the internet, however I can't find what I need, and I'm sure it's quite simple to you.
I'm using ASP.Net, and I want to add to my website, multiple items, which every item hold a picture, and some other information, including links. I'm pretty sure I don't need to write the code for every item in the HTML source, and I don't know exactly how to implement my this.
The basic idea is that my items will be imported from a Database that I create in visual studio, and I want to style my webpage so they would appear in a certain formation, I thought I might need to use Javascript or CSS for this, hope I'm not mistaken.
Javascript isn't some sort of magician that will render all your stuff on its own. However, you can use it to attach a template to every of your items.
What you have to do is :
Create a base HTML template for 1 of your item that can be applied to all of them
Create a Javascript function that will attach thoses CSS classes and HTML attributes to every element out of your DB (or you could use a templating frameork .. since there's a lot of them I'll let you look for it on Google. It's pretty easy to use)
On page load or whatever event you want to bind on, you call your function which will attach the CSS and HTML to every element out of your DB and will render it on your page
Enjoy what you've done.
I hope this helps. Good luck ! ;)
I have many view templates where i have inline javascript specific to that view.
for eg:
app/views/something/index.html.haml
.some-id
%h4 Something
#this javascript is not reused anywhere. Used only in this view
:javascript
$(document).ready(function() {
$('.some-id').addClass('something')
})
Is it a good practice to have the views like above?
It is definitely a pain to maintain inline javascript across multiple views. So i started moving them slowly into one single javascript file. But i am not sure if that's a good thing because, all the javascript will now get executed for all the pages.
So which is the best place to put view specific javascript?
If you are worried about page load time, you should use something like loadjs. It is just not maintainable to be place JS in your views like that. For super simple projects then maybe, but in general you should stay away from it.
Another thing to note is that browsers will cache your javascript when it is loaded from a file. They cannot do this when it is inline because the HTML is rendered every page load. So you probably are looking at an overall performance loss when doing it inline, even without loadjs.
So I know there is a few templating engines out there, but I thought I'd ask for someones experience and reccomendation on one that can do what I'm after.
I'd previously made an (intranet) web app that used asp.net editable listviews to take input and used an update panel to make it persist the data regularly (save)
This worked ok but was alot of hackery to get focus to maintain on the ajax postback amongst other things. This is because update panels rewrite the whole section of html on each ajax postback.
So I figured for some more control and a better user experience (and quicker speeds) I'd try a jquery/javascript based templating engine. What i'm doing is making a sort of to do list (its more than that but lets just keep it simple for this example) where someoen fills out a line with an action, due date, and some other fields.
I liked John Resigs Microtemplating engine so I used that. However after working it all out it seems that it re-writes the whole javascript template each time it updates, which is just what I was trying to avoid, as it loses the users focus. I was hoping for something that would just append things onto the end - why do they need to rewrite everything?? I could do as I did with the udpate panel and use some hacking based on the current focus but I would rather avoid this and make it seamless.
am I going to have to manually code this up, or is there a decent way of doing it out there?
Resig's Microtemplating is great. However, like most client-side templating engines all it gives you is a string. How you get that string into the DOM is up to you.
Why do they need to rewrite everything?
The templating engine is not overwriting anything. Rather that is being done by you (however it is you are putting it into the DOM). For example:
var template = Template('template_id');
// The template engine simply generates a string:
var generatedHtml = template({data:'goes here'});
// The engine's work is done at this point.
// Now lets get the string into the DOM:
myElement.innerHTML = generatedHtml; // OVERWRITES
I was hoping for something that would just append things onto the end
Since you are using jQuery it is trivial to append rather than overwrite. Instead of the innerHTML line above you would use jQuery's append method:
$(myElement).append(generatedHtml);
Now, regarding your focus problem, again this is not strictly due to the template engine but rather the nature of DOM manipulation in general. The solution is to force the element you need to have focus to focus:
Lets say your template looked like this, in Resig's microtemplating parlance:
<script type="text/html" id="template_id">
Foo: <input type="text" value="<%= data %>" />
</script>
Continuing the JavaScript example of prior, you focus like this:
$(myElement).append(generatedHtml);
// Focus the just inserted text box:
$(myElement).find('input').focus();
Checking the HTML source of a question I see for instance:
<a id="comments-link-xxxxx" class="comments-link">add comment</a><noscript> JavaScript is needed to access comments.</noscript>
And then in the javascript source:
// Setup our click events..
$().ready(function() {
$("a[id^='comments-link-']").click(function() { comments.show($(this).attr("id").substr("comments-link-".length)); });
});
It seems that all the user click events are binded this way.
The downsides of this approach are obvious for people browsing the site with no javascript but, what are the advantages of adding events dynamically whith javascript over declaring them directly?
You don't have to type the same string over and over again in the HTML (which if nothing else would increase the number of typos to debug)
You can hand over the HTML/CSS to a designer who need not have any javascript skills
You have programmatic control over what callbacks are called and when
It's more elegant because it fits the conceptual separation between layout and behaviour
It's easier to modify and refactor
On the last point, imagine if you wanted to add a "show comments" icon somewhere else in the template. It'd be very easy to bind the same callback to the icon.
Attaching events via the events API instead of in the mark-up is the core of unobtrusive javascript. You are welcome to read this wikipedia article for a complete overview of why unobtrusive javascripting is important.
The same way that you separate styles from mark-up you want to separate scripts from mark-up, including events.
I see this as one of the fundamental principals of good software development:
The separation of presentation and logic.
HTML/CSS is a presentation language essentially. Javascript is for creating logic. It is a good practice to separate any logic from your presentation if possible.
This way you can have a light-weight page where you can handle all your actions via javascript. Instead of having to use loads of different urls and actions embedded into the page, just write one javascript function that finds the link, and hooks it up, no matter where on the page you dump that 'comment' link.
This saves loads of repeating html :)
The only advantage I see is a reduction of the page size, and thus a lower bandwith need.
Edit: As I'm being downvoted, let met explain a more my answer.
My point is that, using a link as an empty anchor is just a bad practice, nothing else! Of course separation of JavaScript logic from HTML is great. Of course it's easier to refactor and debug. But here, it's against the main principle of unobtrusive JavaScript: Gracefull degradation!
A good solution would be to have to possible call of the comments: one through a REAL link that will point to a simple page showing the comment and another which returns only the comments (in a JSON notation or similar format) with the purpose of being called through AJAX to be injected directly in the main page.
Doing so, the method using the AJAX method should also take care of cancelling the other call, to avoid that the user is redirected to the simple page. That would be Unobtrusive JavaScript. Here it's just JavaScript put on a misused anchor tag.