1

*This has to be done in javascript, not jQuery.

What I'm trying to do is grab the innerText of the h4 tag when <a> with the class btn-blue is clicked.

I realize that I could do:

element.parentElement.previousElementSibling.children[0].innerText;

but I'm really wanting something more flexible.

This is what I have so far, and it does work when I run it in the console (chrome), but as soon as I add it to the site, it doesn't pull in the innerText of the h4 tag.

HTML

<div class="border-box clearfix">
    <div class="blah">
        <h4>h4 Title</h4>
        <p>paragraph</p>
    </div>
    <div class="head clearfix">
        <h4>h4 Title</h4>
        <p>paragraph</p>
    </div>
    <p class="float-right">
        <a href="/transactions/" class="btn-blue">anchor tag</a>
    </p>
</div>

JavaScript

    var el = document.getElementsByClassName('head')[0];
    var parent = el.parentElement;

    while(parent && parent !== document.body && parent.className && parent.className.indexOf('head'){
        parent = parent.previousElementSibling;
    }

    var children = parent.childNodes;

        for(i=0; i < children.length; i++){
            var name = children[i].tagName;
                if(name.toLowerCase() === 'h4'){
                    var text = children[i].innerText || children[i].textContent;
                        return text;
                }
        }

I believe the issue is with using parent.className.indexOf('head') in the while loop. What I'm trying to determine is if the element has the class of head, and from the SO questions (javascript - Test if an element contains a class), I've seen I can do indexOf to check and see if head is within the class.

I've also read that I can use .classList.contains('head'), but that doesn't include support for earlier IE browsers.

Two questions:

  1. Am I getting the innerText of the <h4> tag in the best possible way? (this could be better suited for codereview.stackoverflow.com).

  2. What the best way to test if a element has a class within a while loop?

SOLUTION:

I ended up using @Greg Burghardt 's solution solely because it was the easiest for me to tweak and implement for use in Google Tag Manager. The final code looked like this:

function(){
    var element   = {{gtm.element}};
    var elementClass = 'border-box';

    while(element && !((" " + element.className + " ").indexOf(" " + elementClass + " ") > -1)){
        element = element.parentNode;
    }

    return element.querySelector('h4').innerText || element.querySelector('h4').textContent;
}
Community
  • 1
  • 1
Blexy
  • 9,573
  • 6
  • 42
  • 55
  • If anyone would like to play around he may fork [this plunk](http://plnkr.co/edit/yBbY73AIhv55wfu0ehNy?p=preview). – surfmuggle Apr 22 '14 at 20:18
  • Will the `HTML` be always similar the one in your post? (I mean the hierarchy). Are there going to be 1 or multiple `anchor` tags in your page? – W.D. Apr 24 '14 at 20:10
  • @W.D. there will be multiple `anchor` tags throughout the page. Think of the `HTML` block I've provided as a one blog post out of a blog roll (multiple blog post summaries). That make sense? – Blexy Apr 24 '14 at 20:43
  • I've written a code which works in IE8 as well. I'll post it. Take a look at it. I'll post an example on `jsfiddle`, but the latter has no support of IE8 whatsoever. However my code works in IE8 (just tested it). – W.D. Apr 24 '14 at 20:45

3 Answers3

2

How about this?

function findHeaderText(element, className) {
    while (element && !element.classList.contains(className)) {
        element = element.parentNode;
    }

    var heading = element.querySelector("h1, h2, h3, h4, h5, h6"),
        text = heading.innerHTML;

    return text;
}


function handleClick(event) {
    var text = findHeaderText(event.target, "border-box");

    alert(text);
}

The basic idea is to start at the target of the click event, and walk up the document tree until you find an element containing a certain class. From there, use the querySelector method to grab the first heading, then grab its innerHTML or text.

The function could probably use some additional error handling, but I leave that as an exercise for you. :)

Greg Burghardt
  • 17,900
  • 9
  • 49
  • 92
0

Take a look at this code. This might seem a bit extended, but it does the job!

If you need explanation, I'll provide it!

DEMO

HTML

<div class="border-box clearfix">
    <div class="blah">
        <h4>h4 Title</h4>
        <p>paragraph</p>
    </div>
    <div class="head clearfix">
        <h4>h4 Title</h4>
        <p>paragraph</p>
    </div>
    <p class="float-right">
        <a href="#" onclick='return false' class="btn-blue">anchor tag</a>
    </p>
</div>


<div class="border-box clearfix">
    <div class="blah">
        <h4>h4 Title</h4>
        <p>paragraph</p>
    </div>
    <div class="head clearfix">
        <h4>h4 Title2</h4>
        <p>paragraph</p>
    </div>
    <p class="float-right">
        <a href="#" onclick='return false' class="btn-blue">anchor tag</a>
    </p>
</div>

Javascript

   function attach_(element,listener,ev,tf){

    if(element.attachEvent) {

        element.attachEvent("on"+listener,ev);

        }else{

        element.addEventListener(listener,ev,tf);

        }

    }

function getCls(clsNm){

    return clS = document.querySelectorAll ? document.querySelectorAll("."+clsNm) : document.getElementsByClassName(clsNm);

    }


function returnH4Text(el){

    wrapper = el.parentNode.parentNode;

    divs = wrapper.getElementsByTagName('div');

    if(divs.length>0){

    for(var i=0;i<divs.length;i++){

        if(divs[i].className.match('head')){

            return h4Text = divs[i].getElementsByTagName('h4')[0].innerText ? divs[i].getElementsByTagName('h4')[0].innerText : divs[i].getElementsByTagName('h4')[0].textContent;

            }

        }

    }

    }

var anchors = getCls('btn-blue');

if(anchors.length>0){

    for(var y=0;y<anchors.length;y++){

attach_(anchors[y],'mousedown',function(event){

    evt = event || window.event;
    trg = evt.target || evt.srcElement;

    alert(returnH4Text(trg));

    },false);

    }

}
W.D.
  • 1,033
  • 1
  • 7
  • 12
0

I prefer to use prototype style. This OO style is easier to use and understand. But unfortunately, you can't add prototype to Element before IE8. So this solution only support IE8+ and other browsers. If you want to support IE7, you can try to change it to functional style. http://jsfiddle.net/Wa629/

Element.prototype.getParentByClassName = function(className) {
    if(!this.parentNode)
        return null;
    else if(this.parentNode.hasClass(className))
        return this.parentNode;
    else
        return this.parentNode.getParentByClassName(className);
};

Element.prototype.hasClass = function(className) {
    return (this.classList || this.className.split(' ')).contains(className);
};

Element.prototype.text = function() {
    return this.innerText || this.textContent;
};

if(!Array.prototype.contains)
{
    Array.prototype.contains = function(item) {
        for(var i = 0;i < item.length;++i)
            if(this[i] == item)
                return true;
        return false;
    }
}

Now, you can use following way to get title:

element.getParentByClassName('border-box').querySelector('.head h4').text()
emn178
  • 1,474
  • 8
  • 12