I'm creating a little drag'n'drop directive for angular which detects URLs dragged from another browser window/tab and just discovered it is possible to also read the original anchor Text with this property:
xMozUrl = event.dataTransfer.getData('text/x-moz-url');
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Recommended_Drag_Types#link
This seems very handy because I can display the link Text
Is there a (bleeding edge) altnernative to Mozilla's custom Event.dataTransfer property on retrieving the actual Link-Text from a dragged anchor? I couldn't find anything on the docs. IE 10/11/Edge is also very appreciated.
For some context, this is the currently the whole 'drop' listener:
$document.bind("drop", function(event) {
event.preventDefault();
event.stopPropagation();
if (event.target == target || event.target.parentNode == target) {
try {
var url,
linkText = '',
xMozUrl = event.dataTransfer.getData('text/x-moz-url');
if (xMozUrl.length > 0) {
// mozilla also sends the link text so we can extract some kind of 'title'
var uriParts = xMozUrl.split("\n");
url = uriParts[0];
linkText = uriParts[1];
} else {
// should return the first valid URL found in dataTransfer
// https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Recommended_Drag_Types#link
url = event.dataTransfer.getData('URL').toString();
title = '';
}
if (url.length > 0) {
$scope.url = url;
$scope.$emit('drag-link:drag:drop', {
url : url,
title : linkText
});
}
} catch (e) {
console.error(e);
}
} else {
console.log("drop", event, event.target, event.toElement, $elem);
}
});
Edit
Just found out by testing that IE 10,11 don't support dragging an anchor to an open Page at all. On 10/11 a the URL is just opened as if there were not event.preventDefault()
Chrome appears to support the 'text/x-moz-url' format.
In the case of EDGE, there is a format called "UserActivityJsonArray". This a JSON object with 'title' and 'uri'. This appears to be a recent developement (see: Windows 10 SDK Preview Build 17125). Whereas I have yet to find anything for IE10/11.
I have HTML form with a textarea and a button which calls the function below when a part of the text is selected. With de id of the clicked button and the selected text I want to call a php script by the POST method.
$(document).ready(function() {
$(".postbit_buttons").on("click", ".quoted_clicked", function() {
var post = this.id;
var selected = GetSelectedText();
alert(" post = " + post + " seleted text = " + selected);
$.post("test_quoted.php", {
pid: post,
selection: selected
})
.done(function() {
alert("done")
});
});
});
Function GetSelectedText() to get the selection was found here.
The first alert is shown with the correct information. However, after clicking OK I get the following error message on the browser console:
TypeError: 'collapse' called on an object that does not implement
interface Selection.
I have used a similar construct in another part of the forum software of which this is a part, and that works.
I have pain in my eyes of staring at this, but cannot find the cause. Is there anybody who can help me on this?
#Rory
The code of function GetSelectedText() is:
function GetSelectedText()
{
var selectedText=(
window.getSelection
?
window.getSelection()
:
document.getSelection
?
document.getSelection()
:
document.selection.createRange().text
);
if(!selectedText || selectedText=="")
{
if(document.activeElement.selectionStart)
{
selectedText = document.activeElement.value.substring(
document.activeElement.selectionStart
. document.activeElement.selectionEnd);
}
}
return selectedText;
}
Thanks to Rory's question I tried things out. I discovered that is was the variable "selected" in which the selected text is stored was the cause of the error. I now use the following version of GetSelectedText():
function GetSelectedText () {
if (window.getSelection) { // all browsers, except IE before version 9
var range = window.getSelection ();
return range.toString ();
}
else {
if (document.selection.createRange) { // Internet Explorer
var range = document.selection.createRange ();
return range.text;
}
}
}
and it works!!
Sorry for bothering you. I do practice programming since 1968, but almost 100% was scientific computing with FORTRAN. Javascript is very new to me.
Regards, Ad Bakker
I'm working on a chrome extension, and I set window.title in the onload handler. It seems, though, that the page I'm modifying sets the document title dynamically as well. There's a huge collection of scripts being linked. Is there any way for me to prevent anyone else from modifying document.title or any of its variants, without knowing where the modification is coming from? Alternatively, is there a quick way for me to see where the change is coming from?
I had same problem, some external scripts are changed my page title by document.title = "..."
I've made own solution for it:
try {
window.originalTitle = document.title; // save for future
Object.defineProperty(document, 'title', {
get: function() {return originalTitle},
set: function() {}
});
} catch (e) {}
See the answer to how to listen for changes to the title element?. Notably:
function titleModified() {
window.alert("Title modifed");
}
window.onload = function() {
var titleEl = document.getElementsByTagName("title")[0];
var docEl = document.documentElement;
if (docEl && docEl.addEventListener) {
docEl.addEventListener("DOMSubtreeModified", function(evt) {
var t = evt.target;
if (t === titleEl || (t.parentNode && t.parentNode === titleEl)) {
titleModified();
}
}, false);
} else {
document.onpropertychange = function() {
if (window.event.propertyName == "title") {
titleModified();
}
};
}
};
This SO answer suggest a technique for how to listen for changes to the document title.
Perhaps you could use that technique to create a callback which changes the title back to whatever you want it to be, as soon as some other script tries to change it.
I'd like to learn how to bind a CNRTL-S or COMMAND-S to call a function that I have on my page which AJAX saves the textarea content's
How can I bind those two commands? I used to use the following when it was just a textarea, but since adding TinyMCE it no longer works. Suggestions?
// Keybind the Control-Save
jQuery('#text_area_content').bind('keydown', 'ctrl+s',function (evt){
saveTextArea();
return false;
});
// Keybind the Meta-Save Mac
jQuery('#text_area_content').bind('keydown', 'meta+s',function (evt){
saveTextArea();
return false;
});
Thanks
To use a custom method for saving, i declare my saving function in the tinymce.init method
tinyMCE.init({
// General options
mode: "none",
/* some standard init params, plugins, ui, custom styles, etc */
save_onsavecallback: saveActiveEditor,
save_oncancelcallback: cancelActiveEditor
});
Then i define the function itself
function saveActiveEditor() {
var activeEditor = tinyMCE.activeEditor;
var saveUrl = "http://my.ajax.path/saveStuff";
var idEditor = activeEditor.id;
var contentEditor = activeEditor.getContent();
/* the next line is for a custom language listbox to edit different locales */
var localeEditor = activeEditor.controlManager.get('lbLanguages').selectedValue;
$.post(saveUrl ,
{ id: idEditor, content: contentEditor, locale: localeEditor },
function(results) {
if (results.Success) {
// switch back to display instead of edit
return false;
}
else {
activeEditor.windowManager.alert('Error saving data');
return false;
}
},
'json'
);
return false;
}
Don't forget to return false to override the default save action that posts back your data to the server.
edit to add: i only let the user change one tinymce instance at a time. You may want to change the locating the current instance to something else :)
edit #2: TinyMce already catches the Ctrl+s binding to process the data. Since it also cleans up html and is able to handle specific rules it's given when saving, the solution i propose is to plug your way of saving in tinyMce instead of fully overriding the Ctrl+s binding
The problem here is that the tinymce iframe does not delegate the events to the parent window. You can define custom_shortcuts in tinymce and/or use the following syntax:
// to delegate it to the parent window i use
var create_keydown_event = function(combo){
var e = { type : 'keydown' }, m = combo.split(/\+/);
for (var i=0, l=m.length; i<l; i++){
switch(m[i]){
case 'ctrl': e.metaKey = true;
case 'alt': case 'shift': e[m[i] + 'Key'] = true; break;
default : e.charCode = e.keyCode = e.which = m[i].toUpperCase().charCodeAt(0);
}
}
return e;
}
var handler = function(){
setTimeout(function(){
var e = create_keydown_event(combo);
window.parent.receiveShortCutEvent(e);
}, 1);
}
//ed.addShortcut(combo, description, handler);
ed.addShortcut('ctrl+s', 'save_shortcut', handler);
in the parent window you need a function receiveShortCutEvent which will sort out what to do with it
How do I test to see if links are external or internal? Please note:
I cannot hard code the local domain.
I cannot test for "http". I could just as easily be linking to my own site with an http absolute link.
I want to use jQuery / javascript, not css.
I suspect the answer lies somewhere in location.href, but the solution evades me.
Thanks!
I know this post is old but it still shows at the top of results so I wanted to offer another approach. I see all the regex checks on an anchor element, but why not just use window.location.host and check against the element's host property?
function link_is_external(link_element) {
return (link_element.host !== window.location.host);
}
With jQuery:
$('a').each(function() {
if (link_is_external(this)) {
// External
}
});
and with plain javascript:
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
if (link_is_external(links[i])) {
// External
}
}
var comp = new RegExp(location.host);
$('a').each(function(){
if(comp.test($(this).attr('href'))){
// a link that contains the current host
$(this).addClass('local');
}
else{
// a link that does not contain the current host
$(this).addClass('external');
}
});
Note: this is just a quick & dirty example. It would match all href="#anchor" links
as external too. It might be improved by doing some extra RegExp checking.
Update 2016-11-17
This question still got a lot of traffic and I was told by a ton of people that this accepted solution will fail on several occasions. As I stated, this was a very quick and dirty answer to show the principal way how to solve this problem. A more sophisticated solution is to use the properties which are accessible on a <a> (anchor) element. Like #Daved already pointed out in this answer, the key is to compare the hostname with the current window.location.hostname. I would prefer to compare the hostname properties, because they never include the port which is included to the host property if it differs from 80.
So here we go:
$( 'a' ).each(function() {
if( location.hostname === this.hostname || !this.hostname.length ) {
$(this).addClass('local');
} else {
$(this).addClass('external');
}
});
State of the art:
Array.from( document.querySelectorAll( 'a' ) ).forEach( a => {
a.classList.add( location.hostname === a.hostname || !a.hostname.length ? 'local' : 'external' );
});
And the no-jQuery way
var nodes = document.getElementsByTagName("a"), i = nodes.length;
var regExp = new RegExp("//" + location.host + "($|/)");
while(i--){
var href = nodes[i].href;
var isLocal = (href.substring(0,4) === "http") ? regExp.test(href) : true;
alert(href + " is " + (isLocal ? "local" : "not local"));
}
All hrefs not beginning with http (http://, https://) are automatically treated as local
var external = RegExp('^((f|ht)tps?:)?//(?!' + location.host + ')');
Usage:
external.test('some url'); // => true or false
Here's a jQuery selector for only external links:
$('a[href^="(http:|https:)?//"])')
A jQuery selector only for internal links (not including hash links within the same page) needs to be a bit more complicated:
$('a:not([href^="(http:|https:)?//"],[href^="#"],[href^="mailto:"])')
Additional filters can be placed inside the :not() condition and separated by additional commas as needed.
http://jsfiddle.net/mblase75/Pavg2/
Alternatively, we can filter internal links using the vanilla JavaScript href property, which is always an absolute URL:
$('a').filter( function(i,el) {
return el.href.indexOf(location.protocol+'//'+location.hostname)===0;
})
http://jsfiddle.net/mblase75/7z6EV/
You forgot one, what if you use a relative path.
forexample: /test
hostname = new RegExp(location.host);
// Act on each link
$('a').each(function(){
// Store current link's url
var url = $(this).attr("href");
// Test if current host (domain) is in it
if(hostname.test(url)){
// If it's local...
$(this).addClass('local');
}
else if(url.slice(0, 1) == "/"){
$(this).addClass('local');
}
else if(url.slice(0, 1) == "#"){
// It's an anchor link
$(this).addClass('anchor');
}
else {
// a link that does not contain the current host
$(this).addClass('external');
}
});
There are also the issue of file downloads .zip (local en external) which could use the classes "local download" or "external download". But didn't found a solution for it yet.
const isExternalLink = (url) => {
const tmp = document.createElement('a');
tmp.href = url;
return tmp.host !== window.location.host;
};
// output: true
console.log(isExternalLink('https://foobar.com'));
console.log(isExternalLink('//foobar.com'));
// output: false
console.log(isExternalLink('https://www.stackoverflow.com'));
console.log(isExternalLink('//www.stackoverflow.com'));
console.log(isExternalLink('/foobar'));
console.log(isExternalLink('#foobar'));
The benefit of using this approach is that:
It would automatically resolve the hostname for relative paths and fragments;
It works with protocol-relative URLs
To demonstrate this, let's look at the following examples:
const lnk = document.createElement('a');
lnk.href = '/foobar';
console.log(lnk.host); // output: 'www.stackoverflow.com'
const lnk = document.createElement('a');
lnk.href = '#foobar';
console.log(lnk.host); // output: 'www.stackoverflow.com'
const lnk = document.createElement('a');
lnk.href = '//www.stackoverflow.com';
console.log(lnk.host); // output: 'www.stackoverflow.com'
With jQuery
jQuery('a').each(function() {
if (this.host !== window.location.host) {
console.log(jQuery(this).attr('href'));
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
You can use is-url-external module.
var isExternal = require('is-url-external');
isExternal('http://stackoverflow.com/questions/2910946'); // true | false
/**
* All DOM url
* [links description]
* #type {[type]}
*/
var links = document.querySelectorAll('a');
/**
* Home Page Url
* [HomeUrl description]
* #type {[type]}
*/
var HomeUrl = 'https://stackoverflow.com/'; // Current Page url by-> window.location.href
links.forEach(function(link) {
link.addEventListener('click', function(e) {
e.preventDefault();
// Make lowercase of urls
var url = link.href.toLowerCase();
var isExternalLink = !url.includes(HomeUrl);
// Check if external or internal
if (isExternalLink) {
if (confirm('it\'s an external link. Are you sure to go?')) {
window.location = link.href;
}
} else {
window.location = link.href;
}
})
})
Internal Link
External Link
This should work for any kind of link on every browser except IE.
// check if link points outside of app - not working in IE
try {
const href = $linkElement.attr('href'),
link = new URL(href, window.location);
if (window.location.host === link.host) {
// same app
} else {
// points outside
}
} catch (e) { // in case IE happens}
Yes, I believe you can retrieve the current domain name with location.href. Another possibility is to create a link element, set the src to / and then retrieving the canonical URL (this will retrieve the base URL if you use one, and not necessarily the domain name).
Also see this post: Get the full URI from the href property of a link
For those interested, I did a ternary version of the if block with a check to see what classes the element has and what class gets attached.
$(document).ready(function () {
$("a").click(function (e) {
var hostname = new RegExp(location.host);
var url = $(this).attr("href");
hostname.test(url) ?
$(this).addClass('local') :
url.slice(0, 1) == "/" && url.slice(-1) == "/" ?
$(this).addClass('localpage') :
url.slice(0, 1) == "#" ?
$(this).addClass('anchor') :
$(this).addClass('external');
var classes = $(this).attr("class");
console.log("Link classes: " + classes);
$(this).hasClass("external") ? googleAnalytics(url) :
$(this).hasClass("anchor") ? console.log("Handle anchor") : console.log("Handle local");
});
});
The google analytics bit can be ignored but this is where you'd probably like to do something with the url now that you know what type of link it is. Just add code inside the ternary block.
If you only want to check 1 type of link then replace the ternaries with an if statement instead.
Edited to add in an issue I came across. Some of my hrefs were "/Courses/" like so. I did a check for a localpage which checks if there is a slash at the start and end of the href. Although just checking for a '/' at the start is probably sufficient.
I use this function for jQuery:
$.fn.isExternal = function() {
var host = window.location.host;
var link = $('<a>', {
href: this.attr('href')
})[0].hostname;
return (link !== host);
};
Usage is: $('a').isExternal();
Example: https://codepen.io/allurewebsolutions/pen/ygJPgV
This doesn't exactly meet the "cannot hardcode my domain" prerequisite of the question, but I found this post searching for a similar solution, and in my case I could hard code my url. My concern was alerting users that they are leaving the site, but not if they are staying on site, including subdomains (example: blog.mysite.com, which would fail in most of these other answers). So here is my solution, which takes some bits from the top voted answers above:
Array.from( document.querySelectorAll( 'a' ) ).forEach( a => {
a.classList.add( a.hostname.includes("mywebsite.com") ? 'local' : 'external' );
});
$("a").on("click", function(event) {
if ($(this).hasClass('local')) {
return;
} else if ($(this).hasClass('external')) {
if (!confirm("You are about leave the <My Website> website.")) {
event.preventDefault();
}
}
});
this works for me:
function strip_scheme( url ) {
return url.replace(/^(?:(?:f|ht)tp(?:s)?\:)?\/\/(www\.)?/g, '');
}
function is_link_external( elem ) {
let domain = strip_scheme( elem.attr('href') );
let host = strip_scheme( window.location.host );
return ! domain.indexOf(host) == 0;
}