Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.javascript > #21711 > unrolled thread

I don't want to have if for each index in the loop

Started by"Tony Johansson" <johansson.andersson@telia.com>
First post2013-10-24 01:47 +0200
Last post2013-10-26 00:38 +0200
Articles 6 — 4 participants

Back to article view | Back to comp.lang.javascript


Contents

  I don't want to have if for each index in the loop "Tony Johansson" <johansson.andersson@telia.com> - 2013-10-24 01:47 +0200
    Re: I don't want to have if for each index in the loop Ben Bacarisse <ben.usenet@bsb.me.uk> - 2013-10-24 02:44 +0100
      Re: I don't want to have if for each index in the loop "Tony Johansson" <johansson.andersson@telia.com> - 2013-10-24 09:16 +0200
        Re: I don't want to have if for each index in the loop Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2013-10-24 12:17 +0200
          Re: I don't want to have if for each index in the loop dhtml <dhtmlkitchen@gmail.com> - 2013-10-25 14:52 -0700
            Re: I don't want to have if for each index in the loop Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2013-10-26 00:38 +0200

#21711 — I don't want to have if for each index in the loop

From"Tony Johansson" <johansson.andersson@telia.com>
Date2013-10-24 01:47 +0200
SubjectI don't want to have if for each index in the loop
Message-ID<l49n7g$u0a$1@dont-email.me>
I have two functions InitThumbnails and ShowBigImage shown below. I want to 
create event handlers for each element when I loop through the thumbnailsArr 
by using only one statement like this
element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[i]); }, false);

In the present code in function InitThumbnails  I have this solution
 if (i == 0)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[0]); }, false);
       else if(i==1)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[1]); }, false);
       else if(i==2)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[2]); }, false);
       else if(i==3)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[3]); }, false);
       else if(i==4)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[4]); }, false);

it works but it's not a nice code and I want to have only one statement like 
I said earlier in this way
element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[i]); }, false);

The problem is that when I test this and use this statement
element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[i]); }, false);
and put a breakpoint on function ShowBigImage the passed parameter image is 
undefined.

I think I need to use something called closure but I don't know how to write 
?

function InitThumbnails(thumbnailsArr)
{
   for (var i = 0; i < thumbnailsArr.length; i++)
   {
       var element = document.getElementById("fig" + i);
       element.setAttribute("src", "img/" + thumbnailsArr[i]);
       element.setAttribute("alt", thumbnailsArr[i].split(".")[0]);
       document.getElementById("desc" + i).innerHTML = 
thumbnailsArr[i].split(".")[0];

       if (i == 0)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[0]); }, false);
       else if(i==1)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[1]); }, false);
       else if(i==2)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[2]); }, false);
       else if(i==3)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[3]); }, false);
       else if(i==4)
           element.addEventListener("click", function () { 
ShowBigImage(thumbnailsArr[4]); }, false);
   }
}

function ShowBigImage(image)
{
    var element = document.getElementById("bigimage");
    element.setAttribute("src", "img/" + image);
    element.setAttribute("alt", image.split(".")[0]);
}

//Tony 

[toc] | [next] | [standalone]


#21712

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2013-10-24 02:44 +0100
Message-ID<0.2881687aedec8c956bad.20131024024418BST.87li1jbfyl.fsf@bsb.me.uk>
In reply to#21711
"Tony Johansson" <johansson.andersson@telia.com> writes:

> I have two functions InitThumbnails and ShowBigImage shown below. I
> want to create event handlers for each element when I loop through the
> thumbnailsArr by using only one statement like this
> element.addEventListener("click", function () {
> ShowBigImage(thumbnailsArr[i]); }, false);
<snip>
> I think I need to use something called closure but I don't know how to
> write ?
>
> function InitThumbnails(thumbnailsArr)
> {
>   for (var i = 0; i < thumbnailsArr.length; i++)
>   {
>       var element = document.getElementById("fig" + i);
>       element.setAttribute("src", "img/" + thumbnailsArr[i]);
>       element.setAttribute("alt", thumbnailsArr[i].split(".")[0]);
>       document.getElementById("desc" + i).innerHTML =
> thumbnailsArr[i].split(".")[0];

    element.addEventListener("click", 
            (function (idx) {
                return function () { ShowBigImage(thumbnailsArr[idx]); }
                })(i),
            false);

>   }
> }

The gist of it (and I am twisting the terminology a lot here) is that in
you previous attempt, all the functions created in the loop capture the
same thing 'i' -- so they share it.  You need a function whose
environment has a binding for a copy of the value of 'i'.  One way to do
that is to call a function that returns the function you need, passing
it the value you want to bind to a new identifier.  Each of the many
event handlers end up with an environment with its own 'idx', each one
with a different value.

By the way, what you wrote before was also a closure -- it simply did
not have the right bindings in its environment.

<snip>
-- 
Ben.

[toc] | [prev] | [next] | [standalone]


#21713

From"Tony Johansson" <johansson.andersson@telia.com>
Date2013-10-24 09:16 +0200
Message-ID<l4ahg3$7ba$1@dont-email.me>
In reply to#21712
Many thanks nice solution and it was exactly this that I was looking for.

//Tony

"Ben Bacarisse" <ben.usenet@bsb.me.uk> skrev i meddelandet 
news:0.2881687aedec8c956bad.20131024024418BST.87li1jbfyl.fsf@bsb.me.uk...
> "Tony Johansson" <johansson.andersson@telia.com> writes:
>
>> I have two functions InitThumbnails and ShowBigImage shown below. I
>> want to create event handlers for each element when I loop through the
>> thumbnailsArr by using only one statement like this
>> element.addEventListener("click", function () {
>> ShowBigImage(thumbnailsArr[i]); }, false);
> <snip>
>> I think I need to use something called closure but I don't know how to
>> write ?
>>
>> function InitThumbnails(thumbnailsArr)
>> {
>>   for (var i = 0; i < thumbnailsArr.length; i++)
>>   {
>>       var element = document.getElementById("fig" + i);
>>       element.setAttribute("src", "img/" + thumbnailsArr[i]);
>>       element.setAttribute("alt", thumbnailsArr[i].split(".")[0]);
>>       document.getElementById("desc" + i).innerHTML =
>> thumbnailsArr[i].split(".")[0];
>
>    element.addEventListener("click",
>            (function (idx) {
>                return function () { ShowBigImage(thumbnailsArr[idx]); }
>                })(i),
>            false);
>
>>   }
>> }
>
> The gist of it (and I am twisting the terminology a lot here) is that in
> you previous attempt, all the functions created in the loop capture the
> same thing 'i' -- so they share it.  You need a function whose
> environment has a binding for a copy of the value of 'i'.  One way to do
> that is to call a function that returns the function you need, passing
> it the value you want to bind to a new identifier.  Each of the many
> event handlers end up with an environment with its own 'idx', each one
> with a different value.
>
> By the way, what you wrote before was also a closure -- it simply did
> not have the right bindings in its environment.
>
> <snip>
> -- 
> Ben. 

[toc] | [prev] | [next] | [standalone]


#21714

FromThomas 'PointedEars' Lahn <PointedEars@web.de>
Date2013-10-24 12:17 +0200
Message-ID<3044571.oA5CmXAGKN@PointedEars.de>
In reply to#21713
Tony Johansson wrote:

> Many thanks nice solution and it was exactly this that I was looking for.
> 
> [Top post]

What you are looking for is

- to read on attribute properties in order to get rid of setAttribute();
- to read on event bubbling in order to get rid of multiple event listeners;
- to learn to quote in order to get read again.

<http://PointedEars.de/faq>

-- 
PointedEars

Twitter: @PointedEars2
Please do not Cc: me. / Bitte keine Kopien per E-Mail.

[toc] | [prev] | [next] | [standalone]


#21732

Fromdhtml <dhtmlkitchen@gmail.com>
Date2013-10-25 14:52 -0700
Message-ID<6ca9a5c5-bfaa-48ca-947e-be5fc60fb3b3@googlegroups.com>
In reply to#21714
On Thursday, October 24, 2013 3:17:34 AM UTC-7, Thomas 'PointedEars' Lahn wrote:
> Tony Johansson wrote:
> 
> 
> 
> > Many thanks nice solution and it was exactly this that I was looking for.
> 
> > 
> 
> > [Top post]
> 
> 
> 
> What you are looking for is
> 
> 
> 
> - to read on attribute properties in order to get rid of setAttribute();
> 
> - to read on event bubbling in order to get rid of multiple event listeners;
> 
Yes! 

Valuable advice here. 

The information on closures is useful to learn, and should be considered here for the OP. And in this particular case, OP, bubbling is more elegant, so look into that.

Here is a general example, though there must be better examples online.

var imgThumbExp = /-thumb$/;
var imgContainer = document.body; // Replace with closer ancestor

imgContainer.onclick = imgContainerClicked;

function imgContainerClicked(ev) {
  var target = ev.target;
  if(target && imgThumbExp.test(target.id)) {
    showBigImage(target.id.replace(imgThumbExp , "");
  }
}

<img id="pretzel-thumb" src="pretzel-thumb.jpg">
<img id="pretzel" src="pretzel.jpg">


> - to learn to quote in order to get read again.
> 
> 
> 
> <http://PointedEars.de/faq>
> 
> 
> 
> -- 
> 
> PointedEars
> 
> 
> 
> Twitter: @PointedEars2
> 
> Please do not Cc: me. / Bitte keine Kopien per E-Mail.

[toc] | [prev] | [next] | [standalone]


#21734

FromThomas 'PointedEars' Lahn <PointedEars@web.de>
Date2013-10-26 00:38 +0200
Message-ID<4429325.cM6MFbt01H@PointedEars.de>
In reply to#21732
dhtml wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Tony Johansson wrote:
>> > Many thanks nice solution and it was exactly this that I was looking
>> > for.
>> > 
>> > [Top post]
>> 
>> What you are looking for is
>> 
>> - to read on attribute properties in order to get rid of setAttribute();
>> - to read on event bubbling in order to get rid of multiple event
>>   listeners;
> 
> Yes!
> 
> Valuable advice here.
> 
> The information on closures is useful to learn, and should be considered
> here for the OP. And in this particular case, OP, bubbling is more
> elegant, so look into that.
> 
> Here is a general example, though there must be better examples online.

Plenty.
 
> var imgThumbExp = /-thumb$/;
> var imgContainer = document.body; // Replace with closer ancestor

The comment should be a multi-line pre-comment to tell it apart of 
deactivating single-line comments.

The comment content is very important here.  The same as a short scope chain 
increases efficiency, so does a short event bubbling route.
 
> imgContainer.onclick = imgContainerClicked;
> 
> function imgContainerClicked(ev) {
>   var target = ev.target;

A wrapper is required to support non-W3C DOM implementations, in particular 
the MSHTML DOM before version 9, or version 9 in Browser Compatibility Mode.

<http://msdn.microsoft.com/en-us/library/ie/ff974946(v=vs.85).aspx>

>   if(target && imgThumbExp.test(target.id)) {
>     showBigImage(target.id.replace(imgThumbExp , "");

Passing ID strings *instead of* DOM object references to functions is not a 
good idea.

>   }
> }
> 
> <img id="pretzel-thumb" src="pretzel-thumb.jpg">
> <img id="pretzel" src="pretzel.jpg">

It is a lot easier (and more efficient) to use the suffix for the *target* 
element of the feature instead of the source element.  And I would filter 
for a class name, if necessary the element type before that:

  /* Replace with closer ancestor */
  var imgContainer = document.body;
  
  imgContainer.onclick = jsx.dom.createEventListener(imgContainerClicked);
  
  function imgContainerClicked (ev)
  {
    var target = ev.target;

    /* A wrapper is required to support non-HTML5 DOM implementations */
    if (target.classList.contains("thumb"))
    {
      showBigImage(document.getElementById(target.id + "-large"));
    }
  }
  
  <img id="pretzel" src="pretzel-thumb.jpg" class="thumb">
  <img id="pretzel-large" src="pretzel.jpg">

See also:

<http://www.w3.org/TR/2012/WD-dom-20121206/#dom-element-classlist>
<http://PointedEars.de/wsvn/JSX/trunk/dom.js>
<http://PointedEars.de/wsvn/JSX/trunk/dom/events.js>

Please trim your quotes, and avoid using the borken Google Groups if 
possible.

<http://PointedEars.de/faq/notes/posting/>

-- 
PointedEars

Twitter: @PointedEars2
Please do not Cc: me. / Bitte keine Kopien per E-Mail.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.javascript


csiph-web