2

How can one (probably using the usual suspect Javascript), have a few (at first non-highlighted links) and then achieve:

Toggle on (make permanent) and off highlighting & differentiate between highlighting and redirect?

  • Clicking on a term makes higlighting permanent (toggle on).
  • Clicking again (after toggle on) on that term redirects to article.
  • Clicking elsewhere removes higlighting (toggle off).

EDIT (to clarify, after question had been put on hold):

Toggle highlighting

I understand this highlighting can be handled using .addClass("active"); and .removeClass("active");, but have no clue on how to include the links when the Class("active") is added and/or how to disable the links when that Class is removed. CSS would then need (e.g.) .active{background:green}

Toggle redirect (link follow)

  • So, it is now figured out preventDefault(); plays a central role in enabling or disabling link following; since, as one can read in its entry in the jQuery-api:
  • If this method is called, the default action of the event will not be triggered.
  • For example, clicked anchors will not take the browser to a new URL.
  • Another way to do this might be using return false;.

The difference between the 2 has to do with propagating (or "bubbling up") the DOM. About this difference, one can read at CSS-tricks or have a look at the answer below by somethinghere, when he adds event.stopImmediatePropagation(); to stop this bubbling up

The difference between the two is the following: return false; also prevents this propagation by itself, whereas preventDefault(); doesn't.

The CSS-tricks-article sais:

function() {return false;}

Is equal to:

function(e) {e.preventDefault(); e.stopPropagation();}

More literature about the difference can be found on StackExchange, which might feature some duplicate posts?

var anchors = document.getElementsByTagName('a'),
    length = anchors.length,
    index;

for (index = 0; index < length; index += 1) {
    anchors[index].onclick = function (e) {
        e.preventDefault();
        if (!(/\bhighlight\b/).test(e.target.className)) {
            e.target.className += ' highlight';
        }
    };
}
.blockElement {
    display:block;
    float: left;
    clear: left;
}
.highlight {
    background-color: yellow;
}
<a href="#one" class="blockElement">Jump to One</a>
<a href="#two" class="blockElement">Jump to Two</a>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<div id="one">One</div>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<div id="two">Two</div>
Community
  • 1
  • 1
O0123
  • 481
  • 9
  • 28
  • Well, there is a question which I am toggling with regarding e.g. [highlighting a current link in JS](http://stackoverflow.com/questions/20003458/highlight-current-link-javascript), but I have no experience with getting a different action a second click. I will suggest myself to take a beginner's *JS* course soon. :) I need more insight in this powerful language. – O0123 Feb 09 '15 at 15:05
  • Ok, but what have you tried? What didn't work? – Xotic750 Feb 09 '15 at 15:08
  • Showing some code that you have tried, even if not working or is awful, would prevent down votes and the question being closed. Now without going back and editing this question in the hope of having it reopened, you have no chance of an answer. – Xotic750 Feb 09 '15 at 15:32
  • @Xotic750 - Ok, thanks. I understand it would be nicer if I had more *JS*-background and fully agree with your advice of course. The problem is I am so ignorant and my knowledge is so limited that, "*me trying something*" in *JS* basically never works! :) – O0123 Feb 09 '15 at 15:36
  • I understand your predicament but you must appreciate that this site is intended for professionals and enthusiasts that are seeking help for a clear problem with the code that they have written. – Xotic750 Feb 09 '15 at 15:41
  • @Xotic750 - I acknowledge your message. The help that I may receive on *Stackexchange*, as the small man and tiniest coder that I am, is very much appreciated. It is amazing to see how experienced people are willing to share their knowledge to help out, just heart-warming. --- I understand it makes things easier when the question-seekers themselves try to study as much as possible as well; your message is received. – O0123 Feb 09 '15 at 16:03
  • 1
    I haven't tested this thoroughly but take a look, at worst you could use it for further questions. http://jsfiddle.net/Xotic750/uysk5jpm/ – Xotic750 Feb 09 '15 at 16:16
  • Oh, one more thing. If you want and intend to accept a jQuery solution then please add that as a tag on your question, otherwise you should expect and accept a vanilla javascript answer. – Xotic750 Feb 09 '15 at 16:25
  • @Xotic750 - Ok, thanks I will sure add the *jQuery tag*. --- Thank you very much for your outstanding help. - Then, when the `classList.add('follow');` appends `follow`, I still have some trouble in following the link afterwards, but can't figure out why (in-code). – O0123 Feb 09 '15 at 16:28
  • Add your code to the question and be specific about the issue, you already have 2 votes to reopen the question, you may get more and even some up votes. ;) – Xotic750 Feb 09 '15 at 16:30
  • @Xotic750 - Ok, thanks. :) I'm trying to understand your code better. --- From the end-user-point of view, I notice that clicking anywhere in the Fiddle's-result screen doesn't remove the highlighting mark-up, one specifically needs to click on existing content (i.c.: the text). --- Further more: when one first clicks an `anchor`, then clicks other content (either another `anchor` or non-anchored content), and then clicks that first `anchor` again, automatically the link is followed. So I guess the code needs the renewed adding of `e.preventDefault();` or work on 1 of its initial conditions. – O0123 Feb 09 '15 at 16:55
  • 1
    Use CSS to set the `body` to be the full page size, using `html`, updated jsFiddle as an example. As I said I have not thoroughly tested it. It's difficult when I can't add a full answer. :) – Xotic750 Feb 09 '15 at 16:56
  • @Xotic750 - Yes, I understand. I think mainly the reason for putting on hold: *"unclear what you're asking by*" is itself vague; as the people answering clearly understood my question. But again: I understand my question had little OP-knowledge to it. --- You fixed the first issue indeed by somehow setting the `body` to full page size. How did you do that then, I can't notice that tweak in the *JSFiddle*? – O0123 Feb 09 '15 at 17:02
  • 1
    Added the extra `preventDefault` that I missed. Basically I added some CSS. Take a look at this question. http://stackoverflow.com/questions/6654958/make-body-have-100-of-the-browser-height – Xotic750 Feb 09 '15 at 17:21
  • See this question for information about understanding closures, which I used in the example for holding the `clicked` state. http://stackoverflow.com/questions/111102/how-do-javascript-closures-work – Xotic750 Feb 09 '15 at 17:32
  • Ok, sorry, now I notice the `100%`; must have been *old browser's cache*. - Thanks for the info about **closures**. As from what I understand at first glance, this seems "let's say: *somewhat*" analogue to the idea of [**nested** intervals](http://en.wikipedia.org/wiki/Nested_intervals) in mathematics. --- Now, coming back to my first response: I can't seem to get the links to work, since when I e.g. replace ` – O0123 Feb 09 '15 at 17:52
  • Well, on jsFiddle and on SO that operation is blocked. You will have to test that from your own web server. – Xotic750 Feb 09 '15 at 17:56
  • @Xotic750 - Ok, understood. Thanks for the info. Nice pedagogical work(-around) with the colors by the way. --- I hope this post gets re-opened soon; as your answer now seems perfectly functional to me, and would add value when posted. – O0123 Feb 09 '15 at 17:59

3 Answers3

2

Here is an example that demonstrates the concept of what you want to achieve. Links and information were posted in comments under the original question.

var anchors = document.getElementsByTagName('a');

document.body.addEventListener('click', function (e) {
    [].forEach.call(anchors, function (anchor) {
        if (e.target !== anchor) {
            anchor.classList.remove('highlight');
            anchor.classList.remove('follow');
        }
    });
}, false);

[].forEach.call(anchors, function (anchor) {
    anchor.addEventListener('click', (function (e) {
        var clicked = false;

        return function (e) {
            if (!clicked) {
                e.preventDefault();
                e.target.classList.add('highlight');
                clicked = true;
            } else {
                clicked = e.target.classList.contains('highlight');
                if (clicked) {
                    e.target.classList.add('follow');
                    e.target.classList.remove('highlight');
                } else {
                    e.preventDefault();
                    e.target.classList.add('highlight')
                    clicked = true;
                }
            }
        };
    }()), false);
});
body {
    position:absolute;
    top:0;
    bottom:0;
    right:0;
    left:0;
}
html {
    height:100%;
}
.blockElement {
    display:block;
    float: left;
    clear: left;
}
.highlight {
    background-color: yellow;
}
.follow {
    background-color: red;
}
<a href="#one" class="blockElement">Jump to One</a>
<a href="#two" class="blockElement">Jump to Two</a>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<div id="one">One</div>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<div id="two">Two</div>
Xotic750
  • 22,914
  • 8
  • 57
  • 79
  • Thank you very much. Very nice work. I have been swamped with many projects lately, but wanted to give my appreciation already for your nice realization. Have a great week. – O0123 Mar 03 '15 at 20:00
1

When you click any link, add the highlight class to it and prevent default to stop following the link if it did not have this class..

/* Take each "a" in your DOM and apply a click handler. */
$("a").click(function(event){
    /* Check if the highlight class is already applied. */
    if(!$(this).hasClass("highlight")){
        /* If not, use preventDefault to stop following the link. */
        event.preventDefault();
        /* Also stop the event from bubbling up to the document as a click. */
        event.stopImmediatePropagation();
        /* Then apply it. */
        $(this).addClass("highlight");
    }
});

When you click anywhere else, check if you are clicking the one with he unique highlighted class. If you aren't, remove the class from any existing link on the page. If you want to prevent any highlighting from happening as something else is highlighted, use a preventDefault() in the if statement. Answer uses jQuery, but it could be done in just javascript.

$(document).click(function(event){
    /* Check if the clicked target has the highlighted class. */
    if(!$(event.target).hasClass("highlight")){
        /* If not, remove the class from any element on the page. */
        $(".highlight").removeClass("highlight");
    }
});

Update

I initially used a global variable to store the current clicked on, but that is unnecessary unless you want to highlight multiple. In that case, make a global variable and use the jQuery add() function to add it to the highlighted.

Update 2

I have shortened the code a bit, cleaned it up. Also, you will have to wrap the whole thing in a $(document).ready(function(){}) as otherwise you will have to add it to the end of your page (as there are no a elements initialised yet).

Update 3

Heres the kicker (thanks for the comments) - when you click on a link you are also clicking on the document. This is because of an effect called Bubbling - the bubble starts at the element thats clicked but it bubbles up the DOM until it hits the window object. I have amended it in the code before, adding event.stopImmediatePropagation(); - which does what it says on the tin - it stops immediate propagation or bubbling.

Heres a snippet:

$(document).ready(function(){
  $(document).click(function(event){
    if(!$(event.target).hasClass("highlight")){
      $(".highlight").removeClass("highlight");
    }
  });
  $("a").each(function(){
    $(this).click(function(event){
      if(!$(this).hasClass("highlight")){
        event.preventDefault();
        event.stopImmediatePropagation();
        $(this).addClass("highlight");
      }
    });
  });
});
.highlight { 
  background: red; 
  color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<a href="http://www.stackoverflow.com">Stack Overflow Highlighting</a>
somethinghere
  • 16,311
  • 2
  • 28
  • 42
  • For removing the class, I think you want to use `if(current != null && current.hasClass("highlighted"))`, instead of using the event's target. For this to work, I'd also initialize current to null. I think null is easier to check than a 0 length array. – Evan OJack Feb 09 '15 at 15:09
  • 1
    Thats why I initialised current as `[]` (which is an array, or an empty jquery object). I have however amended as I didn't need the global anyhow. – somethinghere Feb 09 '15 at 15:10
  • @somethinghere - Thank very much for your reply. I appreciate the sharing of your knowledge. Your code learns me a lot, and when I use this in JSFiddle together with jQuery, the result is just fine regarding to the redirect *after* `addClass("highlight");`. I understand from your code that is since then `.removeClass("highlight");` is executed and thus`event.preventDefault();` is removed? However, this doesn't correlate with the *CSS*'s visible lay-out, the highlighting isn't removed. Clicking elsewhere or on other links accumulates highlighting. --- Any suggestion on what I am missing there? – O0123 Feb 09 '15 at 15:52
  • @somethinghere - So, basicly I don't understand why the *CSS* `.highlight{background-color:green;` isn't removed along with the execution of `.removeClass("highlight");`. --- Further more, If I might ask: why did you choose `higlighted` at 1 instance, whereas all other instances in your code use `higlight`? – O0123 Feb 09 '15 at 15:58
  • 1
    @VincentVerheyen I know why, sorry, I had forgotten about a property called `bubbling`. When you click something, that event fires up through the DOM tree until it reaches the `window`. You need to `stopImmediatePropagation()` to make sure the DOM event doesn't get a bubbled-up click event when you click the link. My post has been amended and tested. Sorry about that. – somethinghere Feb 09 '15 at 16:00
  • @somethinghere - Thank you very much for the great answer, coupled with exquisite pedagogical information. I hope good intentions of people like you keep bubbling up. --- But, then, might I ask: why do you have `highlighted` at 1 instance, whereas all other classes in your code use `higlight`? – O0123 Feb 09 '15 at 16:36
  • @VincentVerheyen Nice catch, I have entirely read over that typo. It happens! And the main problem is that you wouldn't really notice this difference if you only have one link. But once you hve many strange things will start happening so nice catch! I'll amend my post. – somethinghere Feb 09 '15 at 16:46
  • @somethinghere - I am trying to use your code, but might I ask you something, to try to understand it better? I don't understand why after `if(!$(this).hasClass("highlight")){`, you add the `$(this).addClass("highlight");`-class again? Could you help me on understanding that? I wouldn't expect that there. I also don't understand how you remove the class, when the user clicks elsewhere on the screen. --- Many thanks. – O0123 Feb 11 '15 at 15:38
  • The `!` inverts the result of the evaluation, so `!false` becomes `true`, so `!$(this).hasClass("className")` becomes `true` if it _doesn't_ have the classname. Because if will only execute when its `true` and we want to know if the item _doesn't have it_ so we can prevent the click from continuing only when it _does not have the class_. jQuery's `hasClass` returns `true` if it does, so we want to invert that to execute the code. You could just do `$(this).hasClass("name") === false`and it would accomplish the same thing. – somethinghere Feb 11 '15 at 15:52
0

Use an anchor's onclick to run a javascript function. If it hasn't been clicked before, highlight it by changing the css, and return false so the link isn't followed. If it has been clicked before, return true, so the link is followed.

For checking if the link has been 'clicked off of', you may be able to user an onblur event for the anchor.

Evan OJack
  • 567
  • 2
  • 5
  • Thank you very much for your answer. That seems like a nice strategy, and I will dive into this, by doing some *JS*-research. - It seems I need to figure out how one could `return false` or `true`, as you describe it. – O0123 Feb 09 '15 at 15:14
  • It is highly discouraged to use the javascript attributes in your DOM as it significantly reduces reusability once you want to change something. However, you will always need to create some sort of memory that will store that first click, and classes are easy for that. – somethinghere Feb 09 '15 at 15:19