SitePoint Sponsor

User Tag List

Results 1 to 22 of 22
  1. #1
    It's all Geek to me silver trophybronze trophy
    ralph.m's Avatar
    Join Date
    Mar 2009
    Location
    Melbourne, AU
    Posts
    24,097
    Mentioned
    448 Post(s)
    Tagged
    8 Thread(s)

    Target one element inside another

    With HTML like this:

    Code:
    <a href="#">Wikipedia <span>Some extra text here</span></a>
    I'm wondering how best to check for the presence of that <span> inside the <a>. If the <span> were after the <a>, I know I could use something like if(links[i].nextSibling), but it doesn't seem to be as simple when the span is a child of the <a>.

    I tried things like if(links[i].lastChild.nodeType == 1) etc. but I'm just stabbing in the dark, as you can see.
    Facebook | Google+ | Twitter | Web Design Tips | Free Contact Form

    Forum Usage: Tips on posting code samples, images and more

    Forrest Gump: "IE is like a box of chocolates: you never know what you're gonna get."

  2. #2
    SitePoint Member
    Join Date
    Mar 2013
    Location
    Pennsylvania
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Why don't you just use id=""?

  3. #3
    It's all Geek to me silver trophybronze trophy
    ralph.m's Avatar
    Join Date
    Mar 2009
    Location
    Melbourne, AU
    Posts
    24,097
    Mentioned
    448 Post(s)
    Tagged
    8 Thread(s)
    Of course this is easy with CSS, but this example is extracted from a script where the <span> is inserted dynamically, and I was wondering how to check for the existence of that span without having to add an ID to it. It's really just a question about how JS works, rather than an attempt to solve an actual problem.
    Facebook | Google+ | Twitter | Web Design Tips | Free Contact Form

    Forum Usage: Tips on posting code samples, images and more

    Forrest Gump: "IE is like a box of chocolates: you never know what you're gonna get."

  4. #4
    SitePoint Member
    Join Date
    Mar 2013
    Location
    Pennsylvania
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You can add a class to your <a> like <a class="parent"> then do something like $('.parent span') with jquery. I'm assuming you want to use Javascript or jQuery and not CSS based where you posted the q.

  5. #5
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,868
    Mentioned
    206 Post(s)
    Tagged
    12 Thread(s)
    Hi Ralph,

    To check for the presence of the <span> inside the <a> tag, there are a number of possibilities.

    The easiest way would be to get a reference to the links in the document, for example:

    Code JavaScript:
    var links = document.getElementsByTagName("a");
    then call get elementsByTagName on the link in question:

    Code JavaScript:
    var span = links[0].getElementsByTagName("span");
    if (span.length > 0){
      console.log("The might span exists and contains '" + span[0].innerHTML + "'");
    }
    If you want to go the lastChild route, then there is a nice method defined in DOM3 Element Traversal, namely lastElementChild, which will only relate to element nodes, effectively ignoring all other types (i.e. the annoying whitespace text-nodes).

    So, then you can do this:

    Code JavaScript:
    var lastChild = links[0].lastElementChild
    if (lastChild.nodeName == "SPAN"){
      console.log("The might span exists and contains '" + lastChild.innerHTML + "'");
    }
    Example:

    HTML Code:
    <!DOCTYPE HTML>
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Child elements example</title>
      </head>
      
      <body>
        <a href="#">Wikipedia <span>Some extra text here</span></a>
        
        <script>
          var links = document.getElementsByTagName("a");
          var span = links[0].getElementsByTagName("span");
          if (span.length > 0){
            console.log("The might span exists and contains '" + span[0].innerHTML + "'");
          }
          
          var lastChild = links[0].lastElementChild
          if (lastChild.nodeName == "SPAN"){
            console.log("The might span exists and contains '" + lastChild.innerHTML + "'");
          }
        </script>
      </body>
    </html>
    Brothercake did quite a nice article on this recently. Maybe you'll find it useful.

  6. #6
    It's all Geek to me silver trophybronze trophy
    ralph.m's Avatar
    Join Date
    Mar 2009
    Location
    Melbourne, AU
    Posts
    24,097
    Mentioned
    448 Post(s)
    Tagged
    8 Thread(s)
    Thanks a lot for the detailed reply, Dave. Very interesting.

    This arose from an experiment (for learning purposes). I'm following Kevin Yank's JS course at Learnable, and one of the exercises is to create a fancy tooltip with JS. In his example, he wraps a span around the <a> and then places another <span> after the <a>. To test myself, I thought I'd see if I could create a variation, where the tooltip is made simply with a span (absolutely positioned) within the <a> itself.

    It all worked fine, except for one thing. There are two event listeners on the <a>, one for hover and one for focus, and the script makes sure that these two don't conflict (e.g. the focus event doesn't trigger the tooltip if the element is already being hovered). It does this easily by checking if there is already a span after the <a>—using if(link.nextSibling).

    In my case, I have to check if there's a span inside the <a>, and I was wondering if there were a similar shortcut, rather than having to pick through all the <a>s and looking for spans inside them, as you have done. If that's the only way to do it, that's fine, but I thought I'd ask. As least lastElementChild provides some assistance, and I hadn't heard about that one.

    Thanks again. I'm now off to check out Brothercake's article. I do find JS hard to get into, but I'm starting to enjoy it, all the same. Really enjoying event listeners, for some reason.
    Last edited by ScallioXTX; Apr 17, 2013 at 23:48. Reason: user requested change
    Facebook | Google+ | Twitter | Web Design Tips | Free Contact Form

    Forum Usage: Tips on posting code samples, images and more

    Forrest Gump: "IE is like a box of chocolates: you never know what you're gonna get."

  7. #7
    It's all Geek to me silver trophybronze trophy
    ralph.m's Avatar
    Join Date
    Mar 2009
    Location
    Melbourne, AU
    Posts
    24,097
    Mentioned
    448 Post(s)
    Tagged
    8 Thread(s)
    Quote Originally Posted by jacleaves View Post
    You can add a class to your <a> like <a class="parent"> then do something like $('.parent span') with jquery. I'm assuming you want to use Javascript or jQuery and not CSS based where you posted the q.
    Thanks jackleaves. I'm trying to make sure I learn how to do things without jQuery, though it sure does save a lot of work in places (not least with event listeners, if you want IE support). Because I wanted to just check if there was a span there, I wonder if I could use jQuery to make this simple check. Something like

    Code:
    if ($('.parent span')) {}
    That looks a bit messy, but I'll test it out. Feel free to suggest an improvement.

    Edit:

    After some trial and error, this worked:

    Code:
    if (!($('.parent span').length>0))
    Looks pretty messy, though, so perhaps it can be improved?
    Facebook | Google+ | Twitter | Web Design Tips | Free Contact Form

    Forum Usage: Tips on posting code samples, images and more

    Forrest Gump: "IE is like a box of chocolates: you never know what you're gonna get."

  8. #8
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,868
    Mentioned
    206 Post(s)
    Tagged
    12 Thread(s)
    Quote Originally Posted by ralph.m View Post
    In my case, I have to check if there's a span inside the <a>, and I was wondering if there were a similar shortcut, rather than having to pick through all the <a>s and looking for spans inside them, as you have done. If that's the only way to do it, that's fine, but I thought I'd ask. As least lastElementChild provides some assistance, and I hadn't heard about that one.
    There probably is a short cut.
    Could you post a bit of code (i.e. JS, CSS, HTML), so that I can have a look.

    Quote Originally Posted by ralph.m View Post
    Really enjoying event listeners, for some reason.
    Event listeners rock

  9. #9
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,868
    Mentioned
    206 Post(s)
    Tagged
    12 Thread(s)
    Quote Originally Posted by ralph.m View Post
    Looks pretty messy, though, so perhaps it can be improved?[/edit]
    Code JavaScript:
    if($(".parent span").length){
      console.log("Span exists");
    }

  10. #10
    It's all Geek to me silver trophybronze trophy
    ralph.m's Avatar
    Join Date
    Mar 2009
    Location
    Melbourne, AU
    Posts
    24,097
    Mentioned
    448 Post(s)
    Tagged
    8 Thread(s)
    Quote Originally Posted by Pullo View Post
    Could you post a bit of code (i.e. JS, CSS, HTML), so that I can have a look.
    Sure. Here is my working example, prior to your and jackleaves' examples:

    Code:
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <title>Tooltip JS</title>
    <style>
    a {position: relative;}
    a span {position: absolute; left:0; top: 100%; display: block; padding: 10px; background: black; color: white; width: 200px;}
    </style>
    </head>
    <body>
    <h1>Tooltip JS</h1>
    
    <p>This is a paragraph with links to sites like <a href="" title="Wikipedia is a free encyclopedia maintained by the public.">Wikipedia</a>, the magnificent <a href="" title="The SitePoint networks includes articles, learning resources and awesome forums">SitePoint</a>, and our favorite site <a href="" title="A List Apart posts regular articles for people who makes websites.">A List Apart</a>.</p>
    
    <p>More text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text more text.</p>
    
    
    
    <script type="text/javascript">
    
    
    var Tooltips =
    {
    	init: function() // event listener function
    	{
    		var links = document.getElementsByTagName("a");
    		for (i=0, ii=links.length; i<ii; i++) 
    		{
    			if(links[i].title && links[i].title.length > 0)
    			{
    				links[i].addEventListener("mouseover",Tooltips.showTipListener, false);
    				links[i].addEventListener("focus",Tooltips.showTipListener, false);
    				links[i].addEventListener("mouseout",Tooltips.hideTipListener, false);
    				links[i].addEventListener("blur",Tooltips.hideTipListener, false);
    			}
    		}
    	},
    	
    	showTipListener: function(e)
    	{
    		Tooltips.showTip(this);
    		e.preventDefault();
    	},
    	
    	hideTipListener: function(e)
    	{
    		Tooltips.hideTip(this);
    		e.preventDefault();
    	},
    	
    	showTip: function(link) // what happens when the listener detects mouseover or focus event
    	{
    		if (!link.title == "")
    		{
    			var span = document.createElement("span");
    			var tipText = document.createTextNode(link.title);
    			span.appendChild(tipText);
    			link.appendChild(span);
    			link.title = "";
    		}
    	},
    	
    	hideTip: function(link) // what happens when the listener detects mouseout or blur event
    	{
    		if (link.title == "")
    		{
    			var tip = link.lastChild; // really not sure if that will work to target the span
    			link.title = tip.firstChild.nodeValue;
    			link.removeChild(tip);
    		}
    	}
    
    };
    
    Tooltips.init();
    
    </script>
    </body>
    </html>
    You'll see that I started with a workaround for the issue in this thread. Instead of checking for the presence of a <span>, I just check to see if the <a>'s title element is empty:

    Code:
    if (!link.title == "")
    I'm pleased I found a way to make it work, but I'd prefer to check if the span is there or not.

    You can see the need for this by hovering over a link and then tabbing to it. Without the code above, an extra, empty <span> is created when the link has focus. (It's a small point, but an interesting issue. It could be solved with CSS, of course—by hiding span + span—but as I said, this is just a JS learning exercise for me.)
    Facebook | Google+ | Twitter | Web Design Tips | Free Contact Form

    Forum Usage: Tips on posting code samples, images and more

    Forrest Gump: "IE is like a box of chocolates: you never know what you're gonna get."

  11. #11
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,868
    Mentioned
    206 Post(s)
    Tagged
    12 Thread(s)
    Quote Originally Posted by ralph.m View Post
    You'll see that I started with a workaround for the issue in this thread. Instead of checking for the presence of a <span>, I just check to see if the <a>'s title element is empty:

    Code:
    if (!link.title == "")
    I'm pleased I found a way to make it work, but I'd prefer to check if the span is there or not.
    Ok, then why don't you change this:

    Code JavaScript:
    if (!link.title == "")

    into this:

    Code JavaScript:
    if (!link.getElementsByTagName("span").length)

    which will check to ensure that the link which is currently being hovered over or which has focus, doesn't already contain a <span> element.

    Does that help any?

  12. #12
    It's all Geek to me silver trophybronze trophy
    ralph.m's Avatar
    Join Date
    Mar 2009
    Location
    Melbourne, AU
    Posts
    24,097
    Mentioned
    448 Post(s)
    Tagged
    8 Thread(s)
    Quote Originally Posted by Pullo View Post
    change this:

    Code JavaScript:
    if (!link.title == "")

    into this:

    Code JavaScript:
    if (!link.getElementsByTagName("span").length)
    Hah, nice one Pullo. That does the trick, and is much more elegant. Thanks a lot—that's the sort of thing I was after.

    I'm interested to explore why it works with .length at the end, but not without it. Can you only test truth or falsity on a property (.length) and not a method (which I assume is what getElementsByTagName() would be called)?
    Facebook | Google+ | Twitter | Web Design Tips | Free Contact Form

    Forum Usage: Tips on posting code samples, images and more

    Forrest Gump: "IE is like a box of chocolates: you never know what you're gonna get."

  13. #13
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,868
    Mentioned
    206 Post(s)
    Tagged
    12 Thread(s)
    Hi Ralph,

    Quote Originally Posted by ralph.m View Post
    I'm interested to explore why it works with .length at the end, but not without it. Can you only test truth or falsity on a property (.length) and not a method (which I assume is what getElementsByTagName() would be called)?
    Not quite. Whenever JavaScript expects a boolean value (e.g. for the condition of an if statement), any value can be used. It will be interpreted as either true or false. The following values are interpreted as false:

    Code:
    undefined, null
    Boolean: false
    Number: -0, +0, NaN
    String: ''
    All other values are considered true.

    getElementsByTagName() returns a NodeList containing all the matched elements. If no elements are found this will be a list with zero members. If you pass this NodeList to the conditional, it will always evaluate as true no matter what it contains.

    To get around this, you need to pass the length of the NodeList (obtained via its length property) to the conditional instead. If no matches were encountered, then the length will be zero, which evaluates to false, everything else will evaluate to true.

    Does that make sense?

    Here's a couple of links that you might find interesting:
    http://www.2ality.com/2013/04/quirk-...onversion.html
    http://reference.sitepoint.com/javas...mentsByTagName

  14. #14
    It's all Geek to me silver trophybronze trophy
    ralph.m's Avatar
    Join Date
    Mar 2009
    Location
    Melbourne, AU
    Posts
    24,097
    Mentioned
    448 Post(s)
    Tagged
    8 Thread(s)
    Quote Originally Posted by Pullo View Post
    If no elements are found this will be a list with zero members ... you need to pass the length of the NodeList ... If no matches were encountered, then the length will be zero, which evaluates to false ...

    Does that make sense?
    Makes perfect sense. Thanks for the explanation, and nicely explained.

    It has taken me a while to get used to using true and false for conditions, but it is certainly sinking in now.
    Facebook | Google+ | Twitter | Web Design Tips | Free Contact Form

    Forum Usage: Tips on posting code samples, images and more

    Forrest Gump: "IE is like a box of chocolates: you never know what you're gonna get."

  15. #15
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,678
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    If things ever do get to be confusing, you can make things clearer and potentially easier to understand by explicitly showing the condition that you need. For example:

    Code javascript:
    if (link.getElementsByTagName("span").length === 0) {
        ...
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  16. #16
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,868
    Mentioned
    206 Post(s)
    Tagged
    12 Thread(s)
    Hey Paul,

    I should probably know this, but why would you need a triple equals and not a double one?

  17. #17
    It's all Geek to me silver trophybronze trophy
    ralph.m's Avatar
    Join Date
    Mar 2009
    Location
    Melbourne, AU
    Posts
    24,097
    Mentioned
    448 Post(s)
    Tagged
    8 Thread(s)
    Quote Originally Posted by Pullo View Post
    why would you need a triple equals and not a double one?
    I don't know how closely JS issues parallel those of PHP, but here's an interesting thread about the hazards of == in PHP:

    http://www.sitepoint.com/forums/show...m-the-keyboard!
    Facebook | Google+ | Twitter | Web Design Tips | Free Contact Form

    Forum Usage: Tips on posting code samples, images and more

    Forrest Gump: "IE is like a box of chocolates: you never know what you're gonna get."

  18. #18
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,678
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Pullo View Post
    why would you need a triple equals and not a double one?
    Using the triple equals is a best practice, which helps to avoid many problems and issues that can arise with the double equals.

    Douglas Crockford has a very good piece about this in his Progamming Style & Your Brain talk, which starts from Slide #41 of his talk, or from 37:19 of the video.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  19. #19
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,868
    Mentioned
    206 Post(s)
    Tagged
    12 Thread(s)
    Thanks for that.
    I just watched the talk, which was quite entertaining (not to mention informative).

    The fact if if (a == 0) could/should be written if (a === 0) wasn't new to me, as I use JSLint and of course it complains when you use the first version.
    However, I had never really looked into why.

    I did a little more research and found the following in Crockford's JavaScript: The Good Parts, which is the essence of what he is saying in his talk:

    JavaScript has two sets of equality operators: === and !==, and their evil twins == and !=. The good ones work the way you would expect. If the two operands are of the same type and have the same value, then === produces true and !== produces false. The evil twins do the right thing when the operands are of the same type, but if they are of different types, they attempt to coerce the values. The rules by which they do that are complicated and unmemorable. These are some of the interesting cases:

    '' == '0' // false
    0 == '' // true
    0 == '0' // true

    false == 'false' // false
    false == '0' // true

    false == undefined // false
    false == null // false
    null == undefined // true

    ' \t\r\n ' == 0 // true
    The lack of transitivity is alarming. My advice is to never use the evil twins. Instead, always use === and !==. All of the comparisons just shown produce false with the === operator.
    As a side note, it is interesting that different languages have different implementations of the triple equals.
    For example in ruby it is used to mean "if a described a set, would b be a member of that set?"

    Code:
    (1..5) === 3           # => true
    (1..5) === 6           # => false
    
    Integer === 42          # => true
    Integer === 'fourtytwo' # => false
    
    /ell/ === 'Hello'     # => true
    /ell/ === 'Foobar'    # => false
    Anyway, thanks for pointing me towards that talk and thanks for showing me how to link to a specific point in a YouTube video. That's a cool trick

    Ref.
    http://oreilly.com/javascript/excerp...bad-parts.html
    http://stackoverflow.com/questions/4...tor-do-in-ruby

  20. #20
    om nom nom nom Stomme poes's Avatar
    Join Date
    Aug 2007
    Location
    Netherlands
    Posts
    10,266
    Mentioned
    50 Post(s)
    Tagged
    2 Thread(s)
    A note to Ralph: testing focus on your current links may be difficult with an empty href, depending on browser.

    == is nice and useful if you honestly really only want to see if two things are equal. It turns out that most of the time, you really want to know if two things are actually the same.

    I once saw someone put up an example like this:

    "if a person is good enough, and you don't care if it's a man or a woman:

    if (man == woman) {} //true

    but if you must have a man:

    if man === woman) {} //false"

    It was an interesting analogy. Equality doesn't mean identity.

  21. #21
    It's all Geek to me silver trophybronze trophy
    ralph.m's Avatar
    Join Date
    Mar 2009
    Location
    Melbourne, AU
    Posts
    24,097
    Mentioned
    448 Post(s)
    Tagged
    8 Thread(s)
    Quote Originally Posted by Stomme poes View Post
    A note to Ralph: testing focus on your current links may be difficult with an empty href, depending on browser.
    Thanks for the tip, poes. I stripped out the URLs to keep the code sample uncluttered here, but it's good to know for future reference.

    Off Topic:

    Quote Originally Posted by Stomme poes
    (man === woman) {} // false ...
    If only it were that simple ...
    Facebook | Google+ | Twitter | Web Design Tips | Free Contact Form

    Forum Usage: Tips on posting code samples, images and more

    Forrest Gump: "IE is like a box of chocolates: you never know what you're gonna get."

  22. #22
    om nom nom nom Stomme poes's Avatar
    Join Date
    Aug 2007
    Location
    Netherlands
    Posts
    10,266
    Mentioned
    50 Post(s)
    Tagged
    2 Thread(s)
    Off Topic:

    Well the analogy only works in a strict bi-gender world :P


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •