1

My setup: Rails 3.0.9, Ruby 1.9.2, jQuery 1.6.2

HTML

<textarea id="photo-42-9" class="comment_box">Write a comment...</textarea>

jQuery

$('#newsfeed').delegate('.comment_box', 'keydown', function (event){
   if(event.keyCode == 13) {
    event.preventDefault();
    $.post('/comments', { title: ..., description: ... });
  }
});

Rails

comments_controller.rb    
  def create
    @comment = Comment.new

    respond_to do |format|
      format.html # new.html.erb
      format.js
    end
  end

create.js.erb
$("<%= escape_javascript( render 'show_comments') %>").insertBefore(???);

render 'show_comments' returns a <div>stuff</div> that I wish to insert before textarea. I could use the selector on #photo-42-9 but that id is dynamic depending on which textarea element I clicked on. How do I access this object in create.js.erb?

3
  • 1
    How about if you pass the id as a parameter of the post, along with title, description? Commented Aug 30, 2011 at 22:10
  • Guess I could but I am hoping that it is easy to get this object in create.js.erb? Commented Aug 30, 2011 at 22:13
  • Just remembered an even more convenient way to do this, my answer is updated below. Commented Aug 30, 2011 at 22:41

2 Answers 2

5

in js:

$('#newsfeed').delegate('.comment_box', 'keydown', function (event){
   if(event.keyCode == 13) {
    event.preventDefault();
    var params = { title: ..., description: ... };
    var $this = $(event.target);
    $.post('/comments', params, function(response) {
        $this.before(response);
    });
  }
});

and in ruby (i guess, I don't do ruby):

"<%= escape_javascript( render 'show_comments') %>"

As you see, "this" isn't passed to rb. Instead, the $.post function waits for the rb response, and pastes it before the text area (the event target). This way is better as all your js code is actually in your js code.

note: "$(response).insertBefore($this)" would work the same.

note: if you move the $this assigination out of the "if", you can easily check if the fresh comment is not empty and different than the ones already existing (event.keyCode == 13 && $this.val() != '' && $this.val() != $this.prev().text()). As well, you can empty the textarea with $this.val('') right after the "$this.before" insertion.

Sign up to request clarification or add additional context in comments.

3 Comments

Your solution works and I agree, it's cleaner in a way. It had occurred to me if this should be the preferred approach, ie, keep JS code in JS and off Rails as much as possible. Couple of questions, why couldn't I use $(this) instead of having to do var $this = $(event.target)? Also JS didn't properly render the escaped JS output from Rails, is there a JS call to handle that?
you can't use "$(this)" directly, as "this" refers to the event itself (the keydown), and not the target of keydown. But, the target is included in the event, accessible with "event.target". Acutally, the target (your "this") is passed to the "delegate" function (or, click, bind, whatever), who doesn't pass it through the callback function (the one we wrote). Concerning, the rendering of the rail's output, i can't really help you. Did you try $this.before(unescape(response)); ?
Thanks for the explanation. I tried unescape before with no luck, thanks for trying though.
2

There is no way to access this -- which is determined in a client-side script -- from the create action on your controller which is executing server side in a different scope and different programming language.

You have two options. Either you can pass selector information along with your request so that it can be determined by the server, OR, you can name your selectors systematically so that your selector will be able to be determined by the properties of the object itself.

The latter is simpler. It looks like your form is posting comments on a photo, so for instance, if you use

div_for @photo

to generate the container for the photos you're commenting on, then you can always access that container using:

dom_id @photo

So then you could use inheritance to select the textarea you want to insert before.

In your create.js.erb:

$(...content...).append('#<%= dom_id @photo %>')

Obviously you'll have to structure that to fit the content that you are interacting with. It may be easier to append to the inside of the outer photo container or to select a comment you want to insertAfter or something like that.

Alternatively, you could put a hidden field in your form containing the generated ID of the form, something like hidden_field_tag :form_id, form_id_goes_here.

Then in your controller set:

@selector = params[:form_id]

And in your create.js.erb:

$(...content...).insertBefore('<%= escape_javascript @selector %>')

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.