-1

I'm taking an introductory Javascript class, and the assignment I'm working on is to expand text by clicking a 'read more' link, change the link to 'read less', and contract the text when clicking the 'read less' link. I've got it almost finished, but I have one issue. The whole div that contains the text is the 'onclick' element, instead of the link. Meaning, anywhere I click inside the div will activate the onclick event. How do I assign the onclick event to the link instead of the entire div?

this.oData = {};

function getData(){
 //mimic ajax request
 return [
   {
     blogID: 0,
     blurb: "blurb 1 sfdasf safs fdsaf asdf asdfa sdf asdf asdf asdfa sdfasd fasdf asdf asdf asdf asdfasdfadsfadsfasdf asdf asdf sdfasd fasd fasdf asdf sadf asdfa sf"
   },
   {
     blogID: 1,
     blurb: "blurb 2 sfdasf safs fdsaf asdf asdfa sdf asdf asdf asdfa sdfasd fasdf asdf asdf asdf asdfasdfadsfadsfasdf asdf asdf sdfasd fasd fasdf asdf sadf asdfa sf"
   },
   {
     blogID: 2,
     blurb: "blurb 3 sfdasf safs fdsaf asdf asdfa sdf asdf asdf asdfa sdfasd fasdf asdf asdf asdf asdfasdfadsfadsfasdf asdf asdf sdfasd fasd fasdf asdf sadf asdfa sf"
   },
   {
     blogID: 3,
     blurb: "blurb 4 sfdasf safs fdsaf asdf asdfa sdf asdf asdf asdfa sdfasd fasdf asdf asdf asdf asdfasdfadsfadsfasdf asdf asdf sdfasd fasd fasdf asdf sadf asdfa sf"
   }
 ];
};

this.oData = getData();

(function injectInitial(){                                      //self-invoking function to inject the initial text into the HTML.
  for(var i=0; i < this.oData.length; i++){
    document.getElementsByTagName("div")[i].innerHTML = this.oData[i].blurb + "...<a href=\"javascript:void(0);\" >Read Less</a>";
    var activeBlurb = document.getElementsByTagName("div")[i];  //set activeBlurb to the div that the for loop is currently on [i]
    activeBlurb.id='blog' + this.oData[i].blogID;               //set the ID for the current div [i], like blog0, blog1, blog2, blog3
    activeBlurb.data = activeBlurb.id;
    // activeBlurb.data = activeBlurb.getElementsByTagName("a");   //set the data attribute of the current div to the info I need to pass(ID of the current div)
    console.log(activeBlurb.data);
    // // console.log(activeBlurb.getElementsByTagName("a"));
    activeBlurb.onclick = function(){                           //set the onclick event to send the ID of the current div(held in this.data) to function toggleMore.
      toggleMore(this.data);
    }
  }
})();

function toggleMore(clicked_id){
  console.log(clicked_id);

  var clickedBlurb = document.getElementById(clicked_id).innerHTML;
  var status = new Boolean(false);
  var snippet = clickedBlurb.slice(-8,-4);

  if (snippet=="Less") {
    status = 'true';
  } else {
    status = 'false';
  }
  injectText(status,clicked_id);
}

function injectText(status, clicked_id) {
    var intHalved;
    var halvedText = "";
    var fullText = "";
    var i = clicked_id.slice(-1);
    fullText = this.oData[i].blurb;
    intHalved = this.oData[i].blurb.length/2;
    halvedText = this.oData[i].blurb.slice(0,intHalved);

    if(status == 'true') { //this is set by the toggleMore function.
      //inject the shortenend text into the innerHTML
      document.getElementsByTagName("div")[i].innerHTML = (halvedText + "...<a href=\"javascript:void(0);\">Read More</a>");
    } else {
      //inject the full-length text into the innerHTML
      document.getElementsByTagName("div")[i].innerHTML = (fullText + "...<a href=\"javascript:void(0);\">Read Less</a>");
    }

    var activeBlurb = document.getElementsByTagName("div")[i];
    activeBlurb.data = activeBlurb.id;                       //set the data attribute of the current div to the info I need to pass(ID of the current div)
    activeBlurb.onclick = function(){                        //set the onclick event to send the ID of the current div(held in this.data) to function toggleMore.
      toggleMore(this.data);
    }
}

1 Answers1

1

Using delegation you can check if target was list:

div.addEventListener('click', (e) => if(e.target.matches('li')){...});
  • I have no idea what you mean by this. How would I implement this method? – dlindner999 Apr 21 '17 at 17:15
  • You dont need for loop to iterate through div. You can use event delegation and act when event matches wanted element. Look [here](http://stackoverflow.com/questions/1687296/what-is-dom-event-delegation) – Ilya Zinkevych Apr 21 '17 at 17:18
  • 1
    @dlindner999 you add an event listener to the div that fires whenever it's clicked, but your function doesn't run unless an li tag specifcally is clicked within that tag. https://davidwalsh.name/event-delegate – zfrisch Apr 21 '17 at 17:19
  • @zfrisch Thx. That's what i meant. – Ilya Zinkevych Apr 21 '17 at 17:21
  • So in my case would it be: activeBlurb.addEventListener("click", function(e){ if (e.target.matches ("a")) { activeBlurb.onclick = function(){ //set the onclick event to send the ID of the current div(held in this.data) to function toggleMore. toggleMore(this.data); } } }); ? I tried this and it did not work. https://codepen.io/dlindner999/pen/NjrWvV?editors=1011 – dlindner999 Apr 21 '17 at 17:58
  • @dlindner999 you have additional event listener remove "activeBlurb.onclick = function(){} " and leave just "toggleMore(this.data);".Mine works: https://codepen.io/ilyaZin/pen/VbjYLq?editors=1011 – Ilya Zinkevych Apr 21 '17 at 18:06
  • @dlindner999 Don't forget edit answer's code and mark it as solved. – Ilya Zinkevych Apr 21 '17 at 18:17
  • @IlyaZinkevych that works in function injectInitial, but for some reason i can't get it to work when I put your code into function injectText. Do i need to do something different? – dlindner999 Apr 21 '17 at 20:31
  • @dlindner999 i edited appearence in injectText with ES6 and it worked sounds like scope problem. Check it out https://codepen.io/ilyaZin/pen/VbjYLq?editors=0010 – Ilya Zinkevych Apr 21 '17 at 20:41
  • What does the **=>** do in injectText? `activeBlurb.addEventListener("click", (e) =>{` Thank you for all your help. I greatly appreciate it! – dlindner999 Apr 21 '17 at 21:05
  • @dlindner999 [Arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) in short they inherit lexical scope from outside. – Ilya Zinkevych Apr 21 '17 at 21:24
  • @IlyaZinkevych my program is working, but I'm getting an error that I think is related to the => function that was used. Here's the error I'm getting: Uncaught TypeError: Cannot read property 'innerHTML' of null at toggleMore (ReadMoreLess - Copy.js:46) Something in the function is setting the ***clicked_id*** variable to null. I copied your codepen text verbatim into a js file and I get the same exact error. When the **=>** returns the argument that was passed into it, it sets clicked_id to null. Any idea how to fix this? Note: the error does NOT appear in the Codepen. Only HTML. – dlindner999 Apr 21 '17 at 22:50
  • @IlyaZinkevych I'm getting an error, so my issue is still not fully resolved. Can you please look at the comment above and see if you might be able to help? =) – dlindner999 Apr 23 '17 at 16:46
  • @dlindner999 show me your code. Thought by this time you should be able to solve task... – Ilya Zinkevych Apr 24 '17 at 10:14
  • @IlyaZinkevych it's all in this codepen. https://codepen.io/dlindner999/pen/GmqZXM?editors=0011 If you open the **browser's** console, and hit **Read More**, and then **Read Less**, it gives the error. – dlindner999 Apr 24 '17 at 21:05