1

I have 1 event listener that is listening to the container element, I want to attach an active class to any of it's children (.container > div.active) when clicked. (So DO NOT attach an active class to .contain > div > ul.active).

My problem is I'm not entirely sure how bubble up the .target to reach the div.diary?

http://jsbin.com/yereramaxe/edit?html,css,js,output

document.querySelector('.contain').addEventListener('click', diary);

function diary(e) {
 if (e.target.className === 'diary') { 
        //?.classList.add('active');
 }
}
* {
  font-size:0;
  padding:0;
  margin:0;
}
.diary {
  display:inline-block;
  background:red;
  width:33.33%;
  box-sizing:border-box;
  padding:10px;
}
li {
  font-size:18px;
}
ul {
  list-style-type: none;
}
div > ul > li {
  display:inline-block;
  width:33.33%;
}
.diary.active {
  background:blue;
}
<div class="contain">

  <div class="diary">
    <ul>
      <li>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </li>
      <li>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </li>
      <li>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </li>
    </ul>
  </div>

  <div class="diary">
    <ul>
      <li>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </li>
      <li>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </li>
      <li>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </li>
    </ul>
  </div>

  <div class="diary">
    <ul>
      <li>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </li>
      <li>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </li>
      <li>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </li>
    </ul>
  </div>

</div>
ditto
  • 5,917
  • 10
  • 51
  • 88

2 Answers2

1

Since e.target will be where the click actually occurred, you can traverse up the parent hierarchy from that element until you find the corresponding parent diary div and then attach the active class to that parent element.

Here's some code that finds a parent with an appropriate classname:

function hasClass(elem, cls) {
    var str = " " + elem.className + " ";
    var testCls = " " + cls + " ";
    return(str.indexOf(testCls) !== -1) ;
}

function addClass(elem, cls) {
    if (!hasClass(elem, cls)) {
        var oldCls = elem.className;
        if (oldCls) {
            oldCls += " ";
        }
        elem.className = oldCls + cls;
    }
}

function removeClass(elem, cls) {
    var str = " " + elem.className + " ";
    elem.className = str.replace(" " + cls + " ", " ").replace(/^\s+|\s+$/g, "");
}

function findParentByClass(node, cls) {
    while (node && !hasClass(node, cls)) {
        node = node.parentNode;
    }
    return node;    
}

Putting that together into your situation would be like this:

document.querySelector('.contain').addEventListener('click', function(e) {
    var diary = findParentByClass(e.target, "diary");
    // add active class to parent diary div
    if (diary) {
        addClass(diary, "active");
    }
});

I'm guessing that you may also want to mark the other .diary divs as no longer active. If that's the case, then you can do this:

document.querySelector('.contain').addEventListener('click', function(e) {
    var actives = document.querySelectorAll('.contain .active');
    for (var i = 0; i < actives.length; i++) {
        removeClass(actives[i], "active");
    }
    var diary = findParentByClass(e.target, "diary");
    // add active class to parent diary div
    if (diary) {
        addClass(diary, "active");
    }
});

You can, of course, substitute the .classList methods in place if these custom addClass(), hasClass() and removeClass() functions if .classList is compatible with your browser support choices.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
0

From your question, what I can understand is, you want to get the parent 'div.diary' of the 'li' you clicked. Try working your way out like this:

//Generic function to find an element or it's first ancestor with a given class name
function findParentWithClass(elem, className) {
    //You can make this much more safer by going only up to a specific parent container
    //If you want to be a bit more safer with checking classname read this http://stackoverflow.com/questions/5898656/test-if-an-element-contains-a-class
    if(elem.className == className) {
        console.log(elem);
        return elem;
    } else {
        findParentWithClass(elem.parentElement, className);
    } 
}

function diary(e) {
    findParentWithClass(e.target, 'diary');
}

document.querySelector('.contain').addEventListener('click', diary);

The function findParentWithClass(element, className) checks if the element has a specific classname, if true, it returns the element, else, it goes to the parent and checks if the parent has the class this will happen recursively until the parent with specific 'className' is found.

Sravan S
  • 136
  • 2
  • 8