Issue with variable scoping

i have a little issue with my variables disappearing from a certain scope. My code is below

$(document).ready(function(){
var t = $("textarea:not(#testimony)"), a = ['.paragraph', '.underline', '.italics', '.bold'], b = ["<p>", "<u>", "<i>", "<b>"];
t.on("focus", function() {
var d = $(this), f = $(this).val(), g = f.substr(f.selectionStart, f.selectionEnd);
for (var i = 0; i < a.length; i++) {
	$(a[i]).bind("click", function(){ alert(f);
		d.val(f.substr(0, f.indexOf(g)) + /*function() {
      return b[i] + $(this).text() + b[i].substr(0, 1) + "/" + b[i].substr(1);
    } +*/ f.substr(f.indexOf(g)));
	});
}

});

});

The f variable disappears once I throw it into that bind method but the alertcall does run when it is called say at the loop entry or in the outer function. I would probably use use or global in PHP but I have no idea how to reference those variables here. Someone please help!

Bin with html can be found here http://jsbin.com/docini/edit?js,console,output

I think there are a couple of issues here. First of all, you’re binding an additional click handler to those a elements each time you focus the textarea, each time with its current value as f. I can’t imagine this is the desired behaviour…? Furthermore, f[0] is the first character of the textarea’s value, so f[0].substr() doesn’t really make sense. And if the textarea has no value, it even yields a type error as f[0] is undefined.

What exactly are you trying to do anyway?

SORRRRRY! Error on my part. Please see modified initial post. I should be using jQuery’s native wrap function to wrap the selected text with the corresponding elements but it doesn’t work for a reason I suspect is the same variable scoping so I have decided to do the wrapping my gaddemn self! Thank you

I have more than one textarea on the live site so the event needs to be bound each time to know who’s buttons were clicked and who’s text area value to alter.

No need to apologise! Anyway the bin gives you the following warning:

Line 10: Don’t make functions within a loop.

Which is exactly what I was telling you above. I’m still not sure what you’re actually trying to achieve, but if you want to access the textarea value within those click handlers, fetch it from there anew each time the handler is called. Like (maybe?)

$('.paragraph, .underline, .italics, .bold').click(function() {
  var val = $("textarea:not(#testimony)").val();

  // etc.
});

Edit after x-post:

Then you’d have to query the context differently, e.g. by a common parent element. The way you’re doing it results in a new event handler each time any textarea gets focused, so if you focus a textarea say 3 times, every single a element will have no less but 3 event handlers attached to it.

With this, how do I know what corresponding value for the b array to return? I’m working with both remember? If a[1] is clicked for instance, return the textarea value wrapped in b[1] as an html tag.

^^^ That’s the desired behaviour.

I don’t see where you’re even accessing that b array. Do you mean that line which is commented out? And no, I do not remember as you didn’t mention it anywhere in this thread (maybe elsewhere?). Please clarify what you’re trying to do, otherwise I can hardly help you.

Yes. That line was commented out because when I click on the “buttons” that have the click event attached to them, it returns the contents of that function as a string. You can uncomment the line and see for yourself. But feel free the uncomment it and see for yourself. I just didn’t mention it because I felt the code logic was self explanatory.

What the logic I have there intends to achieve is:

  1. When a textarea is focused, pick its text content.
  2. On click of on any div with any of the classes in the first array (a):
    (a) Wrap the selected text with the corresponding value from array b
    (b) Let the new value of this active textarea be the previous text plus the wrapped string from (a) above and any other text after it.

Clear enough?

Yes, thanks. As I said, you only need a single click handler for each div. You could then get the corresponding array element like

var tags = ['<p>', '<u>', '<i>', '<b>'];
var currentTag = tags[$(this).index()];

or you could simply give each div a data-tag="p" etc. attribute and access it like

var tag = $(this).attr('data-tag');

If you have multiple such textareas, you can get the corresponding one by wrapping it in a common parent with those divs. Like e.g.

HTML

<div>
  <div class="paragraph" data-tag="p"> hhh </div>
  <div class="bold" data-tag="b"> yyy </div>
  <div class="underline" data-tag="u"> ddd </div>
  <div class="italics" data-tag="i"> nnn </div>
  <textarea></textarea>
</div>
<div>
  <div class="paragraph" data-tag="p"> hhh </div>
  <div class="bold" data-tag="b"> yyy </div>
  <div class="underline" data-tag="u"> ddd </div>
  <div class="italics" data-tag="i"> nnn </div>
  <textarea></textarea>
</div>

JS

$('.paragraph, .underline, .italics, .bold').click(function() {
  var textarea = $(this).nextAll('textarea');
  var start = textarea.get(0).selectionStart;
  var end = textarea.get(0).selectionEnd;
  var value = textarea.val();
  var tag = $(this).attr('data-tag');
  
  textarea.val(
    value.substr(0, start) +
    `<${tag}>` +
    value.substr(start, end - start) +
    `</${tag}>` +
    value.substr(end)
  );
});

Here’s a fiddle. BTW if you want to make your code self-explanatory, I’d suggest to use meaningful variable names and make your code more readable overall with line breaks and proper indentation.

1 Like

Too dumb to grok these parts

value.substr(0, start) +
    `<${tag}>` +
    value.substr(start, end - start) +
    `</${tag}>` +
    value.substr(end)
  );

Secondly. wouldn’t siblings() be sufficient here instead of the nextAll() since we’re just targetting the singular sibling of these buttons?

That’s a new ES6 feature called (a bit misleadingly) template literals… basically just saves you a few quotes and pluses.

Yes, that would work too – note though that .siblings() returns all siblings; the only difference to .nextAll() is that the latter goes only in one direction.

Returns just the direct sibling when you provide the required element as argument to the siblings() method. I don’t know why jQuery has two methods doing the exact same function but I’m more conversant with the siblings() method. All the same, thank you for telling me about the template literals. I’ll look into it soon.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.