IE not marking links as "visited" when rendered via javascript - javascript

I am working with a site where all content is rendered via ajax postbacks using jquery. I am using Ben Alman's hashchange (http://benalman.com/projects/jquery-hashchange-plugin/) to manage the hash history which allows me to bookmark pages, use the back button etc... Everything works perfectly on everything but IE 9 of course. In IE there is a small issue with "visited" links not being marked as visited. You can see that the link turns purple(visited) for a split second after you click it before the new content is loaded. But once you click the back button the link appears as though it has never been visited. Here is a jfiddle example of what I am talking about:
http://jsfiddle.net/7nj3x/3/
Here is the jsfiddle code assuming you have jquery and the hashchange plugin referenced in head:
$(function(){
// Bind an event to window.onhashchange that, when the hash changes, gets the
// hash and adds the class "selected" to any matching nav link.
$(window).hashchange( function(){
alert("Hash changed to:"+location.hash);
var hash = location.hash;
// Set the page title based on the hash.
document.title = 'The hash is ' + ( hash.replace( /^#/, '' ) || 'blank' ) + '.';
//simulate body being rendered by ajax callback
if(hash == ""){
$("body").html("<p id='nav'><a href='#test1'>test 1</a> <a href='#test2'>test 2</a> <a href='#test3'>test 3</a></p>");
}
else{
$("body").html("Right click within this pane and select \"Back\".");
}
})
// Since the event is only triggered when the hash changes, we need to trigger
// the event now, to handle the hash the page may have loaded with.
$(window).hashchange();
});

You can simply use IE conditional comments to load a specific style:
<!--[if IE]>
a:visited {
padding-left: 8px;
background: url(images/checkmark.gif) no-repeat scroll 0 0;
}
<![endif]-->

Why not setup a code block only to be used by IE that sets the value of a hidden input tag to reflect the click behavior. If a link is clicked you could set the value of the input tag equal to that link id and allow you js to update the elements class to reflect the change.
HTML if IE
<input type="hidden" id="clicked_link" />
JQuery JS if IE
$(function() {
$(a).click(function() {
$(this).attr('id').addClass('visited_link_class');
});
});
CSS
.visited_link_class { color:#your color;}

Maybe if you create the proper elements and building a DOM segment before appending it to the document.
Not sure it would work, can't test it here, but here goes my try adapting your code.
$(function(){
// Bind an event to window.onhashchange that, when the hash changes, gets the
// hash and adds the class "selected" to any matching nav link.
$(window).hashchange( function(){
alert("Hash changed to:"+location.hash);
var hash = location.hash;
// Set the page title based on the hash.
document.title = 'The hash is ' + ( hash.replace( /^#/, '' ) || 'blank' ) + '.';
//simulate body being rendered by ajax callback
if(hash == ""){
$("body").html(
$("<p>").id("nav")
.append($("<a>")
.attr("href","#test1")
.text("teste 1"))
.append($("<a>")
.attr("href","#test2")
.text("test 2"))
.append($("<a>")
.attr("href","#test3")
.text("test 3"))
);
}
else{
$("body").text("Right click within this pane and select \"Back\".");
}
})
// Since the event is only triggered when the hash changes, we need to trigger
// the event now, to handle the hash the page may have loaded with.
$(window).hashchange();
});

Try to consider css LVHA roles, which means the order of an a tag pseudo class matters.
First time to define those class:
A:link
A:visited
A:hover
A:active
If this still did not solve your problem, you can use another js router(hashchange): https://github.com/flatiron/director
I used this one a lot and it works perfectly in many situations.

An option would be to also fake the browser history using the HTML5 history API. This way only after deleting the browser history the link will be 'unvisited'.
Like said on this useful page:
[...] method above switches out the URL in the address bar with
'/hello' despite no assets being requested and the window remaining on
the same page. Yet there is a problem here. Upon hitting the back
button we'll find that we don't return to the URL of this article but
instead we'll go back to whatever page we were on before. This is
because replaceState does not manipulate the browser's history, it
simply replaces the current URL in the address bar.
So like also mentioned on that page you'll have to do a:
history.pushState(null, null, hash);

You can simply use IE conditional comments to load a specific style:
<!--[if IE]>
a:visited {
padding-left: 8px;
background: url(images/checkmark.gif) no-repeat scroll 0 0;
}
<![endif]-->

This is a security feature in ie. The functionality of :visited has been restricted in many modern browsers to prevent CSS exploit.
Hence, there's no workaround for this issue.

Related

Trigger an 'onClick' via URL, but do not visit that URL

Trying to launch a click event of .register-btn a nav item when visiting a given URL, but not allow the browser to visit that URL.
So, home.com/memberlogin would remain on home.com ( or redirect to home.com if I must ), and proceed to activate the click of a button.
This is what I have so far, which redirects nowhere as that ended up taking longer than the click event, and it also was quite messy having to load the 404, then wait, then redirect, then wait, then wait for the click event.
I would like something clean and smooth if possible.
jQuery(document).ready(function() {
jQuery(function() {
switch (window.location.pathname) {
case '/memberlogin':
jQuery('.register-btn a').trigger( "click" );
return False;
}
});
});
Probably explained it dreadfully so apologies all - the .register-btn a already exists so I can't create this element, I simply wish to trigger the click for it when visiting a URL/link. Open to suggestions but I assumed something like /memberlogin would suffice, then the link would trigger. The snag is I don't want to "visit" that URL, but use it for the trigger only.
Open to an easier way and tell me if I am asking for something that doesn't work, just figured there must be a way.
Have you tried e.preventDefault() ?
click
and the jQuery:
$('.dontGo').on('click',function(e){
e.preventDefault();
//do stuff
})
fiddle: https://jsfiddle.net/b9x7x4m6/
docs: http://www.w3schools.com/jquery/event_preventdefault.asp
A full javascript solution is (snippet updated as asked):
window.onload = function () {
[].slice.call(document.querySelectorAll('.dontGo')).forEach(function(element, index) {
element.addEventListener('click', function(e) {
e.preventDefault();
alert(e.target.textContent);
}, false);
});
// in order to target a specific URL you may write code like in reported,
// assuming the result is only one element,
// otherwise you need to use the previous [].slice.call(documen.....:
document.querySelectorAll('.dontGo[href="linkedin.com"]')[0].addEventListener('click', function(e) {
e.preventDefault();
alert('Linkedin anchor: ' + e.target.textContent);
}, false);
};
stackoverflow <br/>
google <br/>
linkedin <br/>
twitter <br/>
The querySelector let you select elements in a lot of different ways:
if you need to select an anchor with a specific href value you can write:
document.querySelectorAll('.dontGo[href="linkedin.com"]')
Remember, always, that the result of querySelectorAll is a NodeList array. You can test against the length of such array in order to get, just for instance, only the second element if it exists, like:
var nodEles = document.querySelectorAll('.dontGo[href="linkedin.com"]');
if (nodEles.length > 1) {
nodEles[1]......
}
or you can use the format:
[].slice.call(...).forEach(....
to convert the NodeList to a normal array and than apply the event listener for each element.
Yes, you may prefix the href attribute of anchor tag with an hash (#) to avoid page redirecting. But, in this case, the hash tag is used to jump in another page section and this will change your url.
Simply create a function
function theAction(){
return false;
}
Then your link will be
page name

Chrome ignoring hashes in URL

I've spent quite a while trying to find answers for this issue, but haven't had any success. Basically I need to scroll the user to the contact portion of the website when they go to healthdollars.com/#contact. This works just fine in Safari, but in Chrome I haven't had any luck. I've tried using jQuery/Javascript to force the browser to scroll down, but I haven't been able to.
Does anyone have any ideas? It's driving me crazy - especially since it's such a simple thing to do.
Not a full answer but in Chrome if you disable Javascript I believe you get the desired behavior. This makes me believe that something in your JavaScript is preventing default browser behavior.
It looks to me like the target element doesn't exist when when page first loads. I don't have any problem if I navigate to the page and then add the hash.
if (window.location.hash.length && $(location.hash)) {
window.scrollTo(0, $(location.hash).offset().top)
}
check for a hash, find the element's page offset, and scroll there (x, y).
edit: I noticed that, in fact, the page starts at #contact, then scrolls back to the top. I agree with the other answerer that there's something on your page that's scrolling you to the top. I'd search for that before adding a hack.
You can do this with JS, for example` if you have JQuery.
$(function(){
// get the selector to scroll (#contact)
var $to = $(window.location.hash);
// jquery animate
$('html'/* or body */).animate({ scrollTop: $to.offset().top });
});
The name attribute doesn't exists in HTML 5 so chrome looks to have made the name attribute obsolete when you use the DOCTYPE html.
The other browsers have yet to catch up.
Change
<a name="contact"></a>
to
<a id="contact"></a>
Maybe this workaround with vanilla javascript can be useful:
// Get the HTMLElement that you want to scroll to.
var element = document.querySelector('#contact');
// Stories the height of element in the page.
var elementHeight = element.scrollHeight;
// Get the HTMLElement that will fire the scroll on{event}.
var trigger = document.querySelector('[href="#contact"]');
trigger.addEventListener('click', function (event) {
// Hide the hash from URL.
event.preventDefault();
// Call the scrollTo(width, height) method of window, for example.
window.scrollTo(0, elementHeight);
})

How to use links in Medium Editor?

I've been trying out the excellent Medium Editor. The problem that I've been having is that I can't seem to get links to "work".
At the simplest, here's some HTML/JS to use to demonstrate the problem:
HTML:
<html>
<head>
<script src="//cdn.jsdelivr.net/medium-editor/latest/js/medium-editor.min.js"></script>
<link rel="stylesheet" href="//cdn.jsdelivr.net/medium-editor/latest/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/medium-editor/latest/css/themes/beagle.min.css" type="text/css">
</head>
<body>
<div class='editable'>
Hello world. link
</div>
</body>
</html>
Javascript:
var editor = new MediumEditor('.editable');
This fiddle demonstrates the problem (using the code above).
If you hover on the link, a popup appears.
If you click the link, nothing happens.
If you click the popup, a form appears where you can edit the link.
It seems to me that clicking the link should take me wherever the link's href is targeting. The only way to use the link is to right click and either open in a new tab or new window -- which I don't want to ask my users to do.
I feel like I must be missing something simple in the configuration (either the Anchor Preview Options or the Anchor Form Options). Unfortunately, I'm not seeing it.
In my actual application, I'm not using jQuery, but I am using angularjs. If a strictly Medium Editor answer doesn't exist, I can fall back to using basic JS or anything that angularjs provides.
I've found how to bind event.
Here is full event list https://github.com/yabwe/medium-editor/blob/master/CUSTOM-EVENTS.md
Try to change your code to
var editor = new MediumEditor('.editable')
.subscribe("editableClick", function(e){if (e.target.href) {window.open(e.target.href)}})
https://jsfiddle.net/fhr18gm1/
So medium-editor is built on top of the built-in browser support for contenteditable elements. When you instantiate medium-editor, it will add the contenteditable=true attribute to whatever element(s) you provided it.
By default, since the text is now editable (the contenteditable attribute makes the browser treat it as WYSIWYG text) the browser no longer supports clicking on the links to navigate. So, medium-editor is not blocking these link clicks from happening, the browsers do it inherently as part of making the text editable.
medium-editor has built in extensions for interacting with links:
anchor extension
allows for adding/removing links
anchor-preview extension
shows a tooltip when hovering a link
when the tooltip is clicked, allows for editing the href of the link via the anchor extension
I think the underlying goal of the editor is the misunderstanding here. The editor allows for editing text, and in order to add/remove/update links, you need to be able to click into it without automatically navigating away. This is what I think of as 'edit' mode.
However, the html produced as a result of editing is valid html, and if you take that html and put it inside an element that does NOT have the contenteditable=true attribute, everything will work as expected. I think of this as 'publish mode'
I look at editors like word or google docs, and you see a similar kind of behavior where when you edit the document, the links don't just navigate away when you click on them, you have to actually choose to navigate them through a separate action after you click the link. However, on a 'published' version of the document, clicking the link will actually open a browser window and navigate there.
I think this does make for a good suggestion as an enhancement to the existing anchor-preview extension. Perhaps the tooltip that appears when hovering a link could have multiple options in it (ie Edit Link | Remove Link | Navigate to URL).
tldr;
Links are not navigable on click when 'editing' text in a browser via the built-in WYSIWYG support (contenteditable). When not in 'edit' mode, the links will work as expected.
This could make for a nice enhancement to the medium-editor anchor-preview extension.
Working off some ideas from #Valijon in the comments, I was able to get it to work using the following code:
var iElement = angular.element(mediumEditorElement);
iElement.on('click', function(event) {
if (
event.target && event.target.tagName == 'A' &&
event.target.href && !event.defaultPrevented) {
$window.open(event.target.href, '_blank');
}
});
I think the key is that apparently the editor lets the event propogate to the ancestor elements, so I was able to just listen for the click on the top level editor element.
Here, $window is angular's $window service -- If you're not using angularjs, window would do the trick and I used angular.element to ease the event listener registry, but you could do it the old-fashioned way (or using the JS framework of your choice).
What I really wanted when I asked the question was behavior similar to Google Docs when in "edit" mode (as described by Nate Mielnik). I opened an issue on the Medium Editor tracker and they decided not to implement it as part of the core medium editor, but they noted that they would be happy to have someone add that functionality as an extension.
So, I decided to implement that functionality as an extension as suggested. It can be found as part of MediumTools1. The project is still in very early stages (e.g. I haven't done anything to make the styling look better, or to use better minifying practices, etc. but we'll happily accept Pull Requests for that).
The guts of the code look like this:
var ClassName = {
INNER: 'medium-editor-toolbar-anchor-preview-inner',
INNER_CHANGE: 'medium-editor-toolbar-anchor-preview-inner-change',
INNER_REMOVE: 'medium-editor-toolbar-anchor-preview-inner-remove'
}
var AnchorPreview = MediumEditor.extensions.anchorPreview;
GdocMediumAnchorPreview = MediumEditor.Extension.extend.call(
AnchorPreview, {
/** #override */
getTemplate: function () {
return '<div class="medium-editor-toolbar-anchor-preview">' +
' <a class="' + ClassName.INNER + '"></a>' +
' -' +
' <a class="' + ClassName.INNER_CHANGE + '">Change</a>' +
' |' +
' <a class="' + ClassName.INNER_REMOVE + '">Remove</a>' +
'</div>';
},
/** #override */
createPreview: function () {
var el = this.document.createElement('div');
el.id = 'medium-editor-anchor-preview-' + this.getEditorId();
el.className = 'medium-editor-anchor-preview';
el.innerHTML = this.getTemplate();
var targetBlank =
this.getEditorOption('targetBlank') ||
this.getEditorOption('gdocAnchorTargetBlank');
if (targetBlank) {
el.querySelector('.' + ClassName.INNER).target = '_blank';
}
var changeEl = el.querySelector('.' + ClassName.INNER_CHANGE);
this.on(changeEl, 'click', this.handleClick.bind(this));
var unlinkEl = el.querySelector('.' + ClassName.INNER_REMOVE);
this.on(unlinkEl, 'click', this.handleUnlink.bind(this));
return el;
},
/** Unlink the currently active anchor. */
handleUnlink: function() {
var activeAnchor = this.activeAnchor;
if (activeAnchor) {
this.activeAnchor.outerHTML = this.activeAnchor.innerHTML;
this.hidePreview();
}
}
});
As an explanation, I just use medium's flavor of prototypical inheritance to "subclass" the original/builtin AnchorPreview extension. I override the getTemplate method to add the additional links into the markup. Then I borrowed a lot from the base implementation of getPreview, but I bound new actions to each of the links as appropriate. Finally, I needed to have an action for "unlinking" the link when "Remove" is clicked, so I added a method for that. The unlink method could probably be done a little better using contenteditable magic (to make sure that it is part of the browser's undo stack), but I didn't spend the time to figure that out (though it would make a good Pull Request for anyone interested :-).
1Currently, it's the only part, but I hope that'll change at some point. . .

Displaying a popup with a fixed message for URLs with target="_blank"

We already have a CSS that adds a "new window" icon, indicating that the link will open a new window:
a[target $="_blank"] {
padding-right: 15px;
background: transparent url(http://opi.mt.gov/Images/SiteWide/Icon_External_Link.png) no-repeat center right;
}
Our lawyers want a popup message that states some legal mumbo-jumbo for every external link. Unfortunately, we have an extensive web site with possibly 10,000 external links! It will be prohibitive to find and touch each link to add a class tag, etc.
Is there any way to modify the above code so that the message appears on hover, much like an 'Alt' or 'title' type?
Thanks in advance!
If you are using jQuery, you could add a global function to attach a click event to all your external links based on the proper selector.
for example:
$('a[target="_blank"]').click(function( event ) {
event.preventDefault();
var yesno = confirm("Legal! Sure you want to go to an external source?");
if (yesno) window.open($(this).attr('href'));
});
I'm sure you could do the same with some type of hover message. Just depends on how you would want to render that. I just used an confirm dialog in my example.
Maybe you can use :after css statement, see http://coding.smashingmagazine.com/2011/07/13/learning-to-use-the-before-and-after-pseudo-elements-in-css/
You can do it with JavaScript too, depending on what kind of action you want to do, if you are running on a touch device, you might want to to a confirm dialog. JSFiddle for here.
var myLinks = document.querySelectorAll('a[target $="_blank"]');
for (var i = 0, length = myLinks.length; i < length; i++) {
// You can listen for whatever you want, different for touch
// so you might want to do it on click and do a confirm dialog
myLinks[i].addEventListener('mouseover', handleLegal, false);
}
function handleLegal (e) {
// Do what you want here.
alert('Hi');
}

Javascript substring method assistance

So long story short im working on a web app and using AJAX within it.
I'm trying to disable the default actions of links when clicked, attach a hash value to the link and then remove the "#" from the url.
the problem im having is that, although the hash values are being attached accordingly, the substring method isnt extracting the "#", it extracts the letter after it.....
here is my code. PS, i left my comments inthere so you get where im trying to go with this
so i dont know....my logic or setup may be wrong....
$(document).ready(function(){
//app vars
var mainHash = "index";
var menuBtn = $('.leftButton');
//~~~~~~load the index page at first go.
loadPage();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~menu show/hide
menuBtn.click( function(){
$('#menu').toggleClass();
});
//Menu items on click , disable link default actions.
$('#menu a').click( hijackLinks );
//~~~~~~~~~~~~~~~~~~~~~~~~~~~functions for mobile index load AND hijacking app links to AJAX links.
function loadPage(url){
if( url == undefined){
$('#contentHere').load('index.html #content', hijackLinks);
window.location.hash = mainHash;
} else {
$('#contentHere').load(url + '#content', hijackLinks );
}
}
function hijackLinks(e){
var url = e.target.href;
e.preventDefault();
loadPage(e.target.href);
window.location.hash = $(this).attr("href").substring(1);
}
});
what im wanting is to remove the "#" from the url. What am i doing wrong, what am i not seeing/understanding?
ive tried substring/substr etc and both do the same thing in that no matter what numbers i choose to insert into the substrings params, they remove EVERYTHING BUT the "#" lol....
Thanks in advanced.
Well, you don't really change the link itself, you only change the window.location.hash, and the hash always has a "#" at the beginning.
What you need to do in order to change the entire url (and remove the '#') is to manipulate the browser history.
Although you should know it works only in newer browsers (the exact browser versions are in the link), so if you target your website to older too browsers you might need to think about having a fallback using the hash. If you decide to have such a fallback, I suggest searching for a plugin which does it instead of making it all yourself.

Categories