How to write this simple javascript or jquery? - javascript

I have some nav links like so:
<ul id="nav">
<li>Home
<li>About
<li>Contact
</ul>
How can I add a CSS class called active to the opening <li> tag of the list item that contains the a href whose value matches the current url?
For example, if the current page the user is on is about.html then the nav should look like this:
<ul id="nav">
<li>Home
<li class="active">About
<li>Contact
</ul>
Please note:
the urls can have additional parameters like:
about.html?foo=bar&bar=loo
so whatever is used to detect the url should not take parameters into consideration but just the page name and extensions.
I would prefer to achieve this in plain JavaScipt since I am not using jQuery for anything else on the site, but either is fine.
Edit
The index page had index.html in the url when it's landed on from another page but if the domain is types it shows as:
http://www.sitename.com/
so if no page is specified the active class should be attached to the home list's tag.

jQuery:
if(window.location.pathname === '') {
$('#nav li:first-child').addClass('active');
}
else {
var path = window.location.pathname;
path = path.substr(path.lastIndexOf('/') + 1);
$('#nav li').filter(function(index) {
return path === $(this).children('a').attr('href');
}).addClass('active');
}
Plain JavaScript:
var menu_elements = document.getElementById('nav').children;
if(window.location.pathname === '') {
menu_elements[0].className += ' active';
}
else {
var path = window.location.pathname;
path = path.substr(path.lastIndexOf('/') + 1);
for(var i = menu_elements.length; i--;) {
var element = menu_elements[i];
var a = element.children[0];
if(a.href === path) {
element.className += ' active';
break;
}
}
}
Note: children[] is not supported by FF 3.0. If you experience any problems with children, you can substitute this with an appropriate getElementsByTagName call.

Simple version
window.onload=function() {
var activeLi;
if (location.pathname) {
var fileName = location.pathname.substring(pathname.lastIndexof('/')+1);
/* just take the start -
not handling filenames that are substrings of other filenames
nor filenames with more than one dot. */
fileName = fileName.split('.')[0];
var links = document.getElementById('nav').getElementsByTagName('a');
for (var i=0;i<links.length;i++) {
if (links[i].href.indexOf(fileName)==0) { // starts with filename
activeLi = links[i].parentNode;
break;
}
}
}
else { // no page given
activeLi = document.getElementById('nav').getElementsByTagName('li')[0];
}
if (activeLi) activeLi.className="active";
}
More complex would be to ADD the active to className, but if you do not have other classes on the LI, then it is not needed - but if you use jQuery it is much simpler.

//Get sub-domain url
var currentUrl = window.location.href,
splitUrlArr = currentUrl.replace(/\?.*/,'').split('\/');
subDomainUrl = splitUrlArr[splitUrlArr.length-1];
//if url matches the site home url, add classname 'active' to first li
if(splitUrlArr.join('\/') == currentUrl) {
document.getElementById('nav').getElementsByTagName('li')[0].className = "active";
}else {
//Find the matching href and add className 'active' to its parent li
var targetLi = null;
var links = document.getElementById('nav').getElementsByTagName('a');
for (var i=0; i < links.length; i++) {
if (links[i].href === subDomainUrl) {
targetLi = links[i].parentNode;
break;
}
}
if(targetLi) { targetLi.className = "active"; }
}

Related

Make text into links outside of <a> tags only

Here's my issue. I made a function that resolves links in javascript, but the use-case I'm stuck with is that there may already be HTML in posts with links.
Users can not post true HTML, but moderators and administrators can, meaning I need to handle both cases.
Here's an example of your typical user post HTML:
<div class="teaser">
This is just your normal post http://google.com some other stuff
</div>
And administrator/moderator:
<div class="teaser">
<b>
THIS LINK
</b>
<br><br>
Supplemental reading: Link again
</div>
Normally, I'd use something like
function replaceURLWithHTMLLinks(text) {
var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
return text.replace(exp,"<a href='$1' target='_blank'>$1</a>");
}
c.not('a').each(function() {
var html = $(this).html();
$(this).html(replaceURLWithHTMLLinks(html));
});
But this causes links to be parsed which exist inside of the href property. I need to be able to create links only when they are outside of tags, and it needs to be through all children as you'll notice that is the first child in a mod/admin post (if they so choose).
Mods and admins can put basically any HTML they desire in their posts, so the tag could be anywhere in the post hierarchy which is not at all consistent.
I could just not parse links on admin or mod posts, but sometimes some mods and admins use the proper HTML tags, and sometimes they don't, which is why I'd like to know the proper way of doing this.
Try this:
var exp = /^(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
$('.teaser').each(function() {
var i, words;
$this = $(this);
words = $this.html().split(' ');
for (i = 0; i < words.length; i++) {
if (exp.test(words[i])) {
words[i] = words[i].replace(exp, "<a href='$1' target='_blank'>$1</a>");
}
}
$this.html(words.join(' '));
});
Demo Link
I found the answer here it seems.
filterTeaserLinkContent: function(data) {
var exp = /\b((https?|ftps?|about|bitcoin|git|irc[s6]?):(\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/|magnet:\?(dn|x[lts]|as|kt|mt|tr)=)([^\s()<>]+|\([^\s()<>]+\))+(\([^\s()<>]+\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])/g;
var nodes = data[0].childNodes;
for(var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if(n.nodeType == n.TEXT_NODE || n.nodeName == 'BR') {
var g = n.textContent.match(exp);
while(g) {
var idx=n.textContent.indexOf(g[0]);
var pre=n.textContent.substring(0,idx);
var a=document.createElement("a");
if (!/^[a-z][\w-]+:/.test(g[0])) {
a.href = "http://" + g[0];
} else {
a.href = g[0];
}
a.innerText = g[0];
n.textContent = n.textContent.substring(idx+g[0].length);
n.parentElement.insertBefore(a,n);
g=n.textContent.match(exp);
}
} else {
Board.filterTeaserLinkContent($(n));
}
}
},
filterTeaserContent: function(data) {
// Jam into <div> so we can play with it
var c = $('<div>' + data + '</div>');
// Remove <wbr> tag which breaks links
c.find('wbr').each(function() {
$(this).remove();
});
// Re-parse the HTML after removing <wbr> or else the text nodes won't be joined
c = $('<div>' + c.html() + '</div>');
// I actually forget what this does, but fuck it. Shit.
c.not("div, s, span, a").each(function() {
var content = $(this).contents();
$(this).replaceWith(content);
});
Board.filterTeaserLinkContent(c);
// Remove images in post preview because they don't need to be here...
c.find('img').each(function() {
$(this).remove();
});
// Simplify line breaks
return c.html().replace(/<br ?\/?><br ?\/?>/g, "<br>");
},
This is for use in the 4chan API in case anyone was curious.

Get Html.ActionLink parameters from li element with query

I have this unordered list:
<ul class="list">
<li class="navItem">#Html.ActionLink("Home", "Index", "Home")</li>
<li class="navItem">#Html.ActionLink("Contact", "ContactForm", "Contact")</li>
</ul>
and in my script i want to select the li element by his action and controller name, that i can retrieve data like width and position from the specific li element.
my Function looks like this, but i don't know how the selector has to look like.
function currentItem() {
//Here i get the current view and controller name
var actionName = '#ViewContext.RouteData.Values["Action"].ToString()';
var controllerName = '#ViewContext.RouteData.Values["Controller"].ToString()';
// something like that, putting the link together?
var $currentItem = $('a[href="' + viewController + controllerName + '"]');
// or find all elements by class name an than do something like
var myArray = $('.navItem');
if (myArray[0].Controller == controllerName ||myArray[0].Action == actionName) {
$currentItem = myArray[0];
}
return $currentItem;
}
Thanks for helping.
Franhu
First of all lets consider what HTML we will be working with. A Html.ActionLink will convert into an a tag, so your HTML will look something like this:
<li class="navItem">Home</li>
With that in mind we can iterate the li elements, check their inner a element and match on the href attribute.
So something like this should work:
function currentItem() {
var actionName = '#ViewContext.RouteData.Values["Action"].ToString()';
var controllerName = '#ViewContext.RouteData.Values["Controller"].ToString()';
var listItems = $('.navItem');
for(var i = 0; i < listItems.length; i++){
var item = $(listItems[i]);
var a = item.find("a");
if(a.attr["href"].indexOf(controllerName + "/" + actionName) != -1){
return item;
}
}
}
Note that this will return the matching li element. If you want the matching a element then use return a; instead.

How to change menu li class when div changes

I'm working on a one-page site that has several "articles". Each div has class "article" and id "first", "second", "third", etc. I have a standard menu:
<ul id="nav">
<li id="n1"></li>
<li id="n2"></li>
<li id="n3"></li>
//...etc
</ul>
What I need to do is assign the class "active" to the li tag w/id n1 when the current article has id "first" (and unassign active class from all other li tags); assign active class to li tag w/id n2 when current article has id "second"; etc.
I would appreciate any help... I'm really stuck on this one. TIA for your help.
P.S. I'm using the following code to, in essence, assign an active class to the currently viewed article:
$('#first, #second, #third, ...).bind('inview', function (event, visible) {
if (visible == true) {
$(this).addClass("inview");
} else {
$(this).removeClass("inview");
}
});
There are more ways to do it. This is one, which may give you ideas:
var navrefs= document.getElementById('nav').getElementsByTagName('a');
for (var i=0, len = navrefs.length;i<len;i++){
if (location.hash == navrefs[i].href){
navrefs[i].parentNode.className = 'active';
} else {
navrefs[i].parentNode.className = '';
}
}
Here's an example of the following →
The following assumes you have the class current on your currently active article page:
var articles = document.getElementsByClassName('article'),
al = articles.length,
which, i;
for (i = 0; i < al; i++) {
if (/current/.test(articles[i].className)) {
which = articles[i].id;
}
}
var atags = document.getElementsByTagName('a'),
al2 = atags.length;
for (i = 0; i < al2; i++) {
if (atags[i].href.search(which) > -1) {
atags[i].parentNode.className = 'active';
} else {
atags[i].parentNode.className = '';
}
}
Since it's to achieve a CSS effect, why not just right CSS like this:
.first #n1,
.second #n2,
.third #n3,
...etc
{
CSS for active article
}
and then just make the JS in charge of setting the appropriate class on the wrapper/body, e.g.
<li id="n1"></li>
etc.
...
function setC(str){
document.getElementById('wrapper').className=str;
}
This would would reduce the demand on the JS engine

JavaScript | Grab inner HTML Text

Let's say I have this line somewhere in my code:
<ul id="mobileBtnsBot">
<li>
<span class="alertsBtn"></span><span class="btnText">Alerts & Advisories</span><div class="button_last"></div>
</li>
<li>
<span class="schedBtn"></span><span class="btnText">Schedules</span><div class="button_last"></div>
</li>
<li>
<span class="mapsBtn"></span><span class="btnText">Maps & Stations</span><div class="button_last"></div>
</li>
<li>
<span class="trainBtn"></span><span class="btnText">TrainView</span><div class="button_last"></div>
</li>
<li>
<span class="ntaBtn"></span><span class="btnText">Next To Arrive</span><div class="button_last"></div>
</li>
<li>
<span class="faresBtn"></span><span class="btnText">Fares</span><div class="button_last"></div>
</li>
<li>
<span class="mediaBtn"></span><span class="btnText"># SEPTA</span><div class="button_last"></div>
</li>
<li>
<span class="button_beg"></span><span class="btnText">Find my Location</span><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;
}
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;
});
}
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.
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>
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.
​
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.
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();
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;
}

Editing all external links with javascript

How can I go through all external links in a div with javascript, adding (or appending) a class and alt-text?
I guess I need to fetch all objects inside the div element, then check if each object is a , and check if the href attributen starts with http(s):// (should then be an external link), then add content to the alt and class attribute (if they don't exist create them, if they do exists; append the wanted values).
But, how do I do this in code?
This one is tested:
<style type="text/css">
.AddedClass
{
background-color: #88FF99;
}
</style>
<script type="text/javascript">
window.onload = function ()
{
var re = /^(https?:\/\/[^\/]+).*$/;
var currentHref = window.location.href.replace(re, '$1');
var reLocal = new RegExp('^' + currentHref.replace(/\./, '\\.'));
var linksDiv = document.getElementById("Links");
if (linksDiv == null) return;
var links = linksDiv.getElementsByTagName("a");
for (var i = 0; i < links.length; i++)
{
var href = links[i].href;
if (href == '' || reLocal.test(href) || !/^http/.test(href))
continue;
if (links[i].className != undefined)
{
links[i].className += ' AddedClass';
}
else
{
links[i].className = 'AddedClass';
}
if (links[i].title != undefined && links[i].title != '')
{
links[i].title += ' (outside link)';
}
else
{
links[i].title = 'Outside link';
}
}
}
</script>
<div id="Links">
<a name="_Links"></a>
FOO
FILE
SomeWhere
SomeWhere 2
SomeWhere 3
ElseWhere 1
ElseWhere 2
ElseWhere 3
BAR
Show/Hide
</div>
If you are on an account on a shared server, like http://big-server.com/~UserName/, you might want to hard-code the URL to go beyond the top level. On the other hand, you might want to alter the RE if you want http://foo.my-server.com and http://bar.my-server.com marked as local.
[UPDATE] Improved robustness after good remarks...
I don't highlight FTP or other protocols, they probably deserve a distinct routine.
I think something like this could be a starting point:
var links = document.getElementsByTagName("a"); //use div object here instead of document
for (var i=0; i<links.length; i++)
{
if (links[i].href.substring(0, 5) == 'https')
{
links[i].setAttribute('title', 'abc');
links[i].setAttribute('class', 'abc');
links[i].setAttribute('className', 'abc');
}
}
you could also loop through all the A elements in the document, and check the parent to see if the div is the one you are looking for
This can be accomplished pretty easily with Jquery. You would add this to the onload:
$("div a[href^='http']").each(function() {
$(this).attr("alt",altText);
var oldClassAttributeValue = $(this).attr("class");
if(!oldClassAttributeValue) {
$(this).attr("class",newClassAttributeValue);
}
});
You could modify this to add text. Class can also be modified using the css function.
My (non-framework) approach would be something along the lines of:
window.onload = function(){
targetDiv = document.getElementById("divName");
linksArray = targetDiv.getElementsByTagName("a");
for(i=0;i=linksArray.length;i++){
thisLink = linksArray[i].href;
if(thisLink.substring(4,0) = "http"){
linksArray[i].className += "yourcontent"; //you said append so +=
linksArray[i].alt += "yourcontent";
}
}
}
This is not tested but I would start like this and debug it from here.

Categories