tags:

views:

198

answers:

7

Let's say I have this line somewhere in my code:

<ul id="mobileBtnsBot">
            <li>
                <a href="/m/alert/index.shtml"><span class="alertsBtn"></span><span class="btnText">Alerts & Advisories</span></a><div class="button_last"></div>
            </li>
            <li>
                <a href="/m/alert/index.shtml"><span class="schedBtn"></span><span class="btnText">Schedules</span></a><div class="button_last"></div>
            </li>
            <li>
                <a href="/m/alert/index.shtml"><span class="mapsBtn"></span><span class="btnText">Maps & Stations</span></a><div class="button_last"></div>
            </li>
            <li>
                <a href="/m/alert/index.shtml"><span class="trainBtn"></span><span class="btnText">TrainView</span></a><div class="button_last"></div>
            </li>
            <li>
                <a href="/m/alert/index.shtml"><span class="ntaBtn"></span><span class="btnText">Next To Arrive</span></a><div class="button_last"></div>
            </li>
            <li>
                <a href="/m/alert/index.shtml"><span class="faresBtn"></span><span class="btnText">Fares</span></a><div class="button_last"></div>
            </li>
            <li>
                <a href="/m/alert/index.shtml"><span class="mediaBtn"></span><span class="btnText">@ SEPTA</span></a><div class="button_last"></div>
            </li>
            <li>
                <a href="/m/alert/index.shtml"><span class="button_beg"></span><span class="btnText">Find my Location</span></a><div class="button_last"></div>
            </li>
        </ul>

I want to use JavaScript to find the <a> holding the text Find my location and hide it according to whichever user-agent your on.

I know you are not supposed to use user-agents as such but I can't use any server-side languages.

If anyone knows how to accomplish this or has a better idea, please share.

EDIT: This page is being created from a web form in Alfresco CMS. If I give it an ID they all get the ID.

isBrowser.js

if (navigator.userAgent.indexOf('Gecko')!= -1
         || navigator.userAgent.indexOf('MSIE')!= -1 || navigator.userAgent.indexOf('Opera')!= -1 || navigator.userAgent.indexOf('Chrome')!= -1) {
    document.write('<link rel="stylesheet" href="/m/css/smartmobile.css" type="text/css" />');
}
else if (navigator.userAgent.indexOf('webkit')!= -1) {
    document.write('<link rel="stylesheet" href="/m/css/smartmobile.css" type="text/css" />');
}
else{
  alert("load mobile css");
  document.write('<link rel="stylesheet" href="/m/css/mobile.css" type="text/css" />');
    function hideListItem(text)
    {
        var ul = document.getElementById("mobileBtnsBot");
        alert("line1");
        for(var i = 0; i < ul.childNodes.length; i++)
        {
          alert("line2-loop");
            var li = ul.childNodes[i];
    alert("line3-loop");
            // Element node.
            if (li.nodeType == 1)
            {
              alert("line4-loop");
                // Find the text in all of the inner-html.
                if (li.innerHTML.indexOf(text) != -1)
                {
                  alert("line5-loop");
                    li.style.display = "none";
                    break;
                }
                alert("line6-loop");
            }
            alert("line7-loop");
        }
        alert("line8");
    }
     hideListItem("Find my Location");
}

mobile-script.js

window.onload = function () {
setTimeout(function(){window.scrollTo(0, 1);}, 100);
var linkElementLnk = document.getElementById("BackButtonlnk");
linkElementLnk.style.display = 'none';
insert();
}

function insert(){
var linkElement = document.getElementById("BackButton");
var linkElementLnk = document.getElementById("BackButtonlnk");
var loc_array = document.location.href.split('/');

if (loc_array[loc_array.length-3] == "maps"|| loc_array[loc_array.length-2] == "stations" || loc_array[loc_array.length-3] == "stations" )
{
  linkElementLnk.style.display = 'block';
  var newT = document.createTextNode("Stations & Maps");
}
else if (loc_array[loc_array.length-3] == "m")
{
  linkElementLnk.style.display = 'none';
}
else if (loc_array[loc_array.length-3] != "m")
{
    linkElementLnk.style.display = 'block';

     if (loc_array[loc_array.length-2] == "w" || loc_array[loc_array.length-2] == "s" || loc_array[loc_array.length-2] == "h" )
     {
        var newT = document.createTextNode(unescape(capWords(loc_array[loc_array.length-2])));
     }

     else
     {
          if (loc_array[loc_array.length-1] == "index.html" || loc_array[loc_array.length-1] == "index.shtml" || loc_array[loc_array.length-1] == "")
          {
          var newT = document.createTextNode(unescape(capWords(loc_array[loc_array.length-3])));
          }
          else
          {
          var newT = document.createTextNode(unescape(capWords(loc_array[loc_array.length-2])));
          }
     }
}
linkElement.appendChild(newT);
}
function capWords(str){ 
   var words = str.split(" "); 
   for (var i=0 ; i < words.length ; i++){ 
      var testwd = words[i]; 
      var firLet = testwd.substr(0,1); 
      var rest = testwd.substr(1, testwd.length -1) 
      words[i] = firLet.toUpperCase() + rest 
   }
   return words;
} 
A: 

As for getting at the <a>, you can do this easily with jQuery:

$(".main").hide();

But you have another problem: You cannot access the user-agent from JavaScript. You will have to resort to a server-side script - and since your question mentions that you can't, you're out of luck for hiding the <a>.


Edit: give the <a> a unique ID, like:

<a id='user_agent_thing' ...

Then you can use:

$("#user_agent_thing").hide();

Further edit: you can find the specific element by its text this way:

var span_element = $('span').filter(function() { return $(this).text() == 'Find my Location'; });

Then you can hide the whole thing by doing:

span_element.parent().parent().parent().hide();
George Edison
There are multiple elements using that class.
Bry4n
`.main` is a class, so there can be more than one. This would hide all instance of `.main`, not necessarily what you want.
Peter Ajtai
@Bry4n, @Peter: See my edit.
George Edison
@Hristo: Please see the edit.
George Edison
I can not give a unique ID
Bry4n
@Bry4n: See my *next* edit.
George Edison
`You cannot access the user-agent from JavaScript` -- unless you use `navigator.userAgent` (but don't do it. browser sniffing is generally bad news.)
Stephen P
@Stephen: I stand corrected. But that definitely doesn't change my opinion that it should not be done.
George Edison
@George: I most definitely agree ("generally bad news") In the OP's case some new device will come along and it won't work with his site simply because he hasn't added it to the list of acceptable browser strings.
Stephen P
I do need a better way of checking it. How do I change this javascript into a better way?
Bry4n
+1  A: 

EDIT:

$("#mobileBtns > li:has(span:contains(TrainView))").hide();

Change TrainView to whatever text you want to search for. This selects the li element under mobileBtns that has a span that contains the text TrainView. If you want a non-jquery solution let me know.

function isBrowser(browserName)
{
  var userAgent = navigator.userAgent;

  for(var i = 0; i < browserName.length; i++)
  {
      if(userAgent.indexOf(browserName[i]) != -1)
      {
          return true;
      }
  }

  return false;
}

if(isBrowser(["BlackBerry"]))
{
   document.write('<link rel="stylesheet" href="/m/css/mobile.css" type="text/css" />');
}
else if(isBrowser(["iPhone", "Android", "Gecko", "MSIE", "Chrome", "Opera"]))
{
  document.write('<link rel="stylesheet" href="/m/css/smartmobile.css" type="text/css" />');
}
else
{
  document.write('<link rel="stylesheet" href="/m/css/mobile.css" type="text/css" />');
}

EDIT: You need to wrap the $(...) code after the document has loaded like this:

$(document).ready(function ()
{
    $("#mobileBtns > li:has(span:contains(TrainView))").hide();
});

EDIT 2: Here's a javascript funciton that doesn't use jQuery to find/hide the list item. Replace the $(document).read()... with this code:

function hideListItem(text)
{
    var ul = document.getElementById("mobileBtns");

    for(var i = 0; i < ul.childNodes.length; i++)
    {
        var li = ul.childNodes[i];

        // Element node.
        if (li.nodeType == 1)
        {
            // Find the text in all of the inner-html.
            if (li.innerHTML.indexOf(text) != -1)
            {
                li.style.display = "none";
                break;
            }
        }
    }

}

window.onload = function (e)
{
    hideListItem("Schedules");
};

EDIT 3: Ok I think window.onload isn't supported in your version of the browser. What you can do is move the JavaScript call to hideListItem() code to the end of the body tag:

<html>
<head>
<!-- put your hideListItem function declaration here -->
</head>
<body>

<!--...stuff here...-->

<script type="text/javascript">
hideListItem("Schedules");
</script>

</body>
</html>
TheCloudlessSky
I'm going to try it out first. I like jQuery much more
Bry4n
Can the down-voter give a reason? This solution works great and isn't subject to random performance issues.
TheCloudlessSky
I like this one best but with the cleaner HTML i produced would it still work?
Bry4n
@Bry4n - I've edited my response with your edit request.
TheCloudlessSky
Thanks I'll try this and respond back, at the latest tomorrow then eventually pick one of these answers as the ultimate solution.Will that fit within the JavaScript? Or should I come up with a different user-agent solution
Bry4n
Good luck @Bry4n. I've also updated my answer to fix-up your browser detection script.
TheCloudlessSky
I tested it using this `if (navigator.userAgent.indexOf('Gecko')!= -1) { document.write('<link rel="stylesheet" href="/m/css/smartmobile.css" type="text/css" />'); $("#mobileBtns > li:has(span:contains(Find my Location))").hide();}` it didn't work (The code quotes aren't working on it)
Bry4n
@Cloudless Where did you update a browser detection script?
Bry4n
@Bry4n - What about it didn't work? See my edit above. I've created a sample on JS Bin. http://jsbin.com/arana4
TheCloudlessSky
I need it within the browser recognition code though. If your on a blackberry or else the link isn't supposed to show.
Bry4n
@Bry4n - Move the `isBrowser` function to your external JS file.
TheCloudlessSky
@Cloudless If you don't mind me asking, what will moving it have to do with anything?
Bry4n
@Bry4n - Just move the function declaration. That way you keep it into external JS that can be cached by the browser.
TheCloudlessSky
@Cloudless Ah good point. I will try it and let you know as soon as I see it working. Thank you for your patience.
Bry4n
@Bry4n - Anytime.
TheCloudlessSky
Your the man, I came into work today and I wrapped it and it worked! Thank you so much.
Bry4n
@Bry4n - Glad it did. The reason wrapping it worked is because you can't search for an element that hasn't been created yet (DOM tree isn't loaded). If it was *below* the element in question it would work. Cheers!
TheCloudlessSky
@Cloudless I just published it and opened the page on my Blackberry and it didn't hide? Do you have any idea why?
Bry4n
@Bry4n - Can you see if jQuery is being loaded? Can you see if something is fired in the "if browser is blackberry..." code (just do an `alert()` or something). HTH
TheCloudlessSky
I just found out BlackBerry doesn't load jQuery smoothly at all which means it will remain shown.
Bry4n
@Bry4n - Try the **EDIT 2** that I've made; it doesn't use jQuery to do searching.
TheCloudlessSky
@CloudlessSky I think it has more to do with this `isBrowser` function. Since checking the browsers has been turned into jQuery it is not even firing nor firing the jQuery to hide the element. Know what I mean? The isBrowser function needs to be replaced by a JavaScript version.
Bry4n
@CloudlessSky that JS didn't work either after changing isBrowser to a JavaScript readable function :(
Bry4n
@Bry4n - Are you sure that the "if blackberry" statement is being called when on a BlackBerry? I don't have an emulator to test with.
TheCloudlessSky
@CloudlessSky Yes, I placed an alert inside of the if statement and it came up on my BlackBerry.
Bry4n
@Bry4n - Can you put some alerts in the other code and see which line it's failing at?
TheCloudlessSky
Okay I placed a bunch of alerts in there and none of them are called after the function. The function isn't even being fired. the window.onload isn't being fired within the JavaScript isBrowser JavaScript function I have. I will post above.
Bry4n
@Bry4n - See my **EDIT 3** above.
TheCloudlessSky
@CloudlessSky Okay we made progress, now it's dying at the for-loop. alert1 pops up but nothing after that so now the function is being called.
Bry4n
@Bry4n - What does it do just before the `for loop` if you do `alert(ul.childNodes); alert(ul.childNodes.length)`?
TheCloudlessSky
Nothing happens. it just says alert1 then breaks and my stylesheet doesn't even show. Above is current JavaScript.
Bry4n
What about `alert(ul)`? Are you sure you have the right id in the `getElementById()`, they're different in the above HTML and JS.
TheCloudlessSky
@CloudlessSky Yeah I changed it because there were two of the same IDs on the page and I already checked that. I pasted above the two JS files I have. I made them include so that I didn't have to regenerate every page if I made a change. when doing `alert(ul);` it returns `null`
Bry4n
@Bry4n - Can you add a *button* to fire the `hideListItem(text)`? This way we can rule out the loading of the DOM issue.
TheCloudlessSky
@CloudlessSky Hmm...interesting. I did what you said and in my Firefox browser it executed without an issue and hid the element. However in my Blackberry clicking the button doesn't do a damn thing.EDIT: The `alert(ul)` came up like this in Firefox - `[object HTMLUListElement]`
Bry4n
@Bry4n - Yeah I've had the same results in FF all along which is why I can't help you too much. I think in all honesty you should post this at the BB dev forums for help since they'd know much more about this (http://supportforums.blackberry.com/t5/Web-Development/bd-p/browser_dev). Sorry I couldn't help you further!
TheCloudlessSky
@CloudlessSky Ugh BB is the worst. Why can't it read basic JavaScript. When Blackberry 6 comes out I hope everyone ditches the older phone (like the one I currently own)
Bry4n
A: 

Without assingning extra id you can make it like that:

 var spans = document.getElementsByTagName('span');
 for(i = 0; i < spans.length; i++)
    if(spans[i].innerText == 'Find my location')
    {
     var aElement = spans[i];
        while(aElement.tagName != 'a')
      aElement = aElement.parentElement;

     aElement.style.display = 'none';
    }

This is pure JS solution, search for the span with the text, then follow up until finds the link element and hides it.

ŁukaszW.pl
Performance-wise, this is terrible. What if there are a lot of spans on the page that have nothing to do with "Find my location".
TheCloudlessSky
Ryan Kinal solution has the same problem. What if thre are many links on the page? And what do you think selectors does? It also goes throught many of spans and compares it content...
ŁukaszW.pl
And the minus for what?
ŁukaszW.pl
You also have the possibility to hit an exception. If a span has 'Find my location' and ISN'T wrapped in an `<a>`.
TheCloudlessSky
+1 - Without knowing absolutely anything else about the element, the only solution is to go through each element and search.
Anurag
This didn't work with the new HTML. I know I probably shouldn't have changed it but it had to be done more cleanly. If only I asked the question afterward.
Bry4n
+2  A: 

Fairly simple, though not exactly efficient. This function will hide any anchor that has p_text inside it. Simply call hide('Find my Location') to accomplish what you want.

var hide = function(p_text, p_elem)
{
    var elem = (p_elem) ? p_elem : document,
        anchors = elem.getElementsByTagName('a'), i = 0;

    for (i = 0; i < anchors.length; i++)
    {
        if (anchors[i].innerHTML.indexOf(p_text) >= 0)
        {
            anchors[i].style.display = 'none';
        }
    }
}

As for the user-agent, feature detection is the way to go, but if you're not browser-detecting to use different features, you may have to actually sniff the UA. Feature detection is great. It really is. When you're using the features you're detecting. But user-agent sniffing has its place, and this may be one of those cases.

[waits for the flame]

Edit: Added optional "p_elem" argument. If you have a container element, you can search that element only by calling hide('Find my Location', containerElement) where containerElement is a DOM node.

Ryan Kinal
Was just about to submit something very similar.
cmptrgeekken
-1 because you should be iterating over *all* the anchor tags!
TheCloudlessSky
@TheCloudlessSky - Explain, please? I'm pretty sure I am iterating over all the anchor tags.
Ryan Kinal
It's basically being used to show different styles based on the type of mobile phone.
Bry4n
@Ryan - Sorry I meant "shouldn't"... not "should".
TheCloudlessSky
@TheCloudlessSky - That's true. It would be more efficient if it didn't have to iterate over all of the anchors. However, with vanilla JavaScript there isn't a cross-browser compatible way to get elements by className. Some browsers support it, others don't.
Ryan Kinal
@Ryan - True but anchor tags are common element, I really think it's overkill. If the OP provided a container for the `<a>`'s then they could easily use `someContainer.getElementsByTagName('a');`.
TheCloudlessSky
I'll concede that one :-) +1
Ryan Kinal
Idiomatically, one would write `p_elem = p_elem || document`. For performance reasons, you probably want to store `array.length` (it's not what you think!) and maybe make an array out of `anchors` itself. It may also be useful to break after a match is found.
strager
@TheCloudlessSky, As far as I know, `??` isn't in JavaScript/ECMAScript; the closest thing is `||`, as I stated.
strager
@strager - I've been in C# mode all day and *meant* to type `||`. :)
TheCloudlessSky
@strager - all good points. Improvements can definitely be made. However, I disagree with breaking after a match is found. That is very much situational.
Ryan Kinal
A: 

For the new HTML:

$('#mobileBtns li:contains(Find my Location)').hide();

For the old HTML:

This jQuery snippet should do the trick (test):

$('.main:has(.button_bg > span:contains(Find my Location))').hide();

To do what you probably want, which is guarded against i18n (test):

$('a.main[href$=media/findmy​​​​​​​​]').hide();

The last example shows that you can perform the hiding using CSS3:

a.main[href$=media/findmy] {
    display: none;
}
strager
Can someone please explain why I got a downvote? See the test page; it *does work*, unless I misunderstood the question.
strager
I didn't down vote, but probably cause the OP asked for a Javascript soludion and you gave an `$('.I').('.can:has(jQueries)').delay(NONE!!!)` solution. Seems to be a growing pet peeve of some.
Peter Ajtai
@Peter - It *is* a valid response *if* the OP has jQuery. For others searching this may be their solution if they're already using jQuery.
TheCloudlessSky
This is a valid answer, as the OP did not specify. In comments, the OP did mention jQuery could be used, so a jQuery solution would be acceptable. A vanilla solution may work better, but an optimized and cross-platform one would be very verbose. (That's mostly the point of frameworks in JS.)
strager
@TheCloudlessSky - I'm just taking a guess at the reason. I think the solution's fine personally.
Peter Ajtai
I tried this `$('#mobileBtns:has(.btnText > span:contains(Find my Location))').hide();` and it didn't work (with the new HTML)
Bry4n
@Bry4n, Updated.
strager
+1  A: 

Why are you using user agents instead of just feature detection?

I presume you are looking for geolocation capabilities on the client, so why not simply check that:

function isGeolocationSupported() {
    return !!navigator.geolocation;
}

See the GeoLocation API spec and a HTML5 site with more examples.

Also, I wouldn't add jQuery for this very feature as even 24K is a lot for mobile devices. You can use the Selectors API to query the text inside the span elements using:

var links = document.querySelectorAll("a.main");

for(var i = 0; i <links.length; i++) {
    var link = links[i];
    var span = link.querySelector('span');
    var text = span.firstChild.nodeValue;
    if(text == 'Find my Location') {
        link.style.visibility = 'hidden';
    }
}

Here's the same without the Selectors API.

var canQueryByClass = 'getElementsByClassName' in document;
var canQueryByTag = 'getElementsByTagName' in document;

if(!(canQueryByClass && canQueryByTag)) {
    // hopeless client, no need to check further.
}

var links = document.getElementsByClassName("main");

for(var i = 0; i <links.length; i++) {
    var link = links[i];
    var span = link.getElementsByTagName('span');
    if(span.length) {
        var text = span.firstChild.nodeValue;
        if(text == 'Find my Location') {
            link.style.visibility = 'hidden';
        }
    }
}

See a working example. ​

Anurag
Using `querySelectorAll` with throw an exception on browsers that don't support it. This is a strictly HTML5 solution...
TheCloudlessSky
The `geolocation` part - yes. The `querySelector` part - no. The Selectors API is easily replaceable with `getElementsByClassName` and `getElementsByTagName`. Also, if a browser does not support the [Selectors API](http://www.quirksmode.org/dom/w3c_core.html#t13), its very likely that it doesn't have geolocation support either. Object detection for `querySelector(All)` support before actually calling it is trivial, `if(document.querySelector) ..`.
Anurag
`getElementsByClassName` isn't supported in IE8 and below. See http://www.quirksmode.org/dom/w3c_core.html#t11
TheCloudlessSky
@TheCloudlessSky, It's probably only executing on the BlackBerry anyway.
strager
@strager - Could be true, but it will still throw an exception if it's not supported.
TheCloudlessSky
+4  A: 

Here's a nice short one.

function findLink (text) {
  var i=-1, v, r=[];
  while (v=document.links[++i]) if ((''+v.innerHTML).match(text)) r.push(v);
  return r;
}

It will turn an array of all <a> elements containing text. text can be a plain string or a regular expression.


Here's a version with an optional callback function. It will be called on all matching links. The first (and only) argument to the function is the <a> element. You can return false from your callback to stop finding links.

function findLink (text, callback) {
  var i=-1, v, r=[], cb=callback||new Function;
  while (v=document.links[++i]) if ((''+v.innerHTML).match(text)) {
    r.push(v); 
    if (cb(v)===false) return r;
  }
  return r;
}

So, hiding the link according to the useragent sounds like a bad idea, but if you're dead set on it, you could do something like this (if I read your question right):

if ((''+navigator.userAgent).match(/BlackBerry|Android|iPhone/)) {
  document.body.innerHTML+='<link rel="stylesheet" href="/m/css/mobile.css" type="text/css" />';
  findLink('Find my Location', function (link) { 
    link.style.display='none'; 
    return false;
  });
}
no
This is some of the most obfuscated javscript I've ever seen...
TheCloudlessSky
how do you figure? I thought it was pretty straightforward.
no
Well you could add some braces for starters.
TheCloudlessSky
I suppose I could, but then I'd feel compelled to split that stuff onto multiple lines... why bother with all that when it's fine as it is?
no
Readability for other's looking at your code? What's the big deal with multiple lines? :)
TheCloudlessSky
@no Are you an old school C programmer in hiding? (regarding the long lines that do a lot of stuff)
Peter Ajtai
I was able to follow it just fine. As far as obfuscation goes, this is not it.
jason
@jason - Seriously...? You must read a lot of C...?
TheCloudlessSky
@Cloudless: I find it easier to read on one line, without `{}`. I guess it's subjective. @Peter, maybe... ;)
no
@no - Yeah without `{}` it's easier, but to me it still seems obfuscated... :)
TheCloudlessSky
Using the `Function` constructor? Ehh... ;P I also don't like your style of iteration, because most JS out there doesn't do it this way. I like your way of solving the problem, though.
strager
@strager: `new Function` could go, might be less (micro)optimal than `function(){}` but to me it's more readable. I like the `var i=-1,v;while(v=o[++i])` style of iteration bc it's like a `foreach`; it gives a value `v` so you don't have to do `o[i]` inside your loop. "Most js" is not a representation of _good_ js. Not that this is especially good, just that most is especially bad.
no
@no, I meant "most JS" for readability reasons. And in this case, I don't think there's anything very wrong about the common JS approach of traditional C-style looping. I think it'd be easier to understand that pattern than the one you have for someone new to the language.
strager