Prevent link dragging, but still allow text highlighting - javascript

I have some data in a table where clicking it will navigate you elsewhere, but people are requesting the ability to highlight the text to be able to copy/paste it elsewhere. Since they are links, the default behavior in HTML is to drag the link... I don't know why or how that is useful, but I want to disable that on certain links.
TL;DR: I want to be able to highlight the text of a link and not drag it.
The gif below should help explain my issue.
The following methods are NOT what I want:
I have seen examples that prevent both highlighting & dragging using something like this
<a draggable="false" href="#">
or this
.no-drag {
user-drag: none;
}
Or this
myElement.ondragstart = function () {
return false;
};
But obviously that is not what I need here.Is what I want possible to do?

In Google Chrome this works
user-select: none;
-webkit-user-drag: none;

#Julien Grégoire's answer above put me on the right track for this, but the below code is the basics of what I ended up using.
var clickedEl = document.getElementById("test");
var limit = 5;
var mouseMoved = false;
function resetEvents() {
clickedEl.onmousemove = null;
clickedEl.ondragstart = null;
clickedEl.onmouseleave = null;
mouseMoved = false;
}
clickedEl.onmousedown = function (downEvent) {
if (clickedEl.attributes.href) {
clickedEl.onclick = function (clickEvent) {
if (mouseMoved) {
clickEvent.preventDefault();
}
resetEvents();
};
}
clickedEl.onmouseleave = function () {
resetEvents();
};
clickedEl.onmousemove = function (moveEvent) {
// This prevents the text selection being dragged
clickedEl.ondragstart = function (dragEvent) {
dragEvent.preventDefault();
};
if (Math.abs(moveEvent.x - downEvent.x) >= limit || Math.abs(moveEvent.y - downEvent.y) >= limit) {
// If user clicks then moves the mouse within a certain limit, select the text inside
window.getSelection().selectAllChildren(clickedEl);
mouseMoved = true;
}
};
};
<a id="test" href="http://stackoverflow.com">Click or select</a>

I'm super late to answer but I'm just gonna leave it here:
Just put draggable="false" inside <a> tag,
<a draggable="false" href="./"></a>
then in CSS you put:
body {
-webkit-user-drag: none;
}

You could detect if user moves the mouse after the click and if so manage selection using window.getSelection. Something like this for example:
var linkEl = document.getElementById('test')
linkEl.onmousedown = function(downEvent) {
var clickedEl = downEvent.target;
var mouseMoved = false;
clickedEl.onmousemove = function() {
// If user clicks then moves, select the whole link
window.getSelection().selectAllChildren(clickedEl);
// Set a flag to prevent opening the link
mouseMoved = true;
// Reset mousemove, else it'll run constantly
clickedEl.onmousemove = null;
// This is only to prevent having the text selection being dragged
clickedEl.ondragstart = function(dragEvent) {
dragEvent.preventDefault();
}
}
if (mouseMoved) {
// If mouse has moved, prevent default
downEvent.preventDefault();
}
}
<a draggable="false" id="test" href="http://stackoverflow.com">Click or select</a>

This is the simplest solution that worked for me. You can change '*' to 'a'.
*, *::after, *::before {
-webkit-user-select: none;
-webkit-user-drag: none;
-webkit-app-region: no-drag;
}

Related

Why is the imagine not displayed while beeing "inline". js/html

I want to show and hide a picture by using one button. when it's clicked, the picture is displayed and a variable is set to 1. so that when you press the button the next time, the picture will be hidden again.
After the button is pressed, I console.log the value of set variable + if the picture is displayed or not. Console says that the Picture is "inline". But the picture is not on my screen.
I think all you need is the js function. If you need more information. just comment. thank's!
<script>
function showHideM(){
let open;
open = 0
if (open == 0){
open = 1;
document.getElementById("melmanId").style.display = "inline";
console.log(open)
console.log(document.getElementById("melmanId").style.display)
return;
}
if (open == 1){
open = 0;
document.getElementById("melmanId").style.display = "none";
}
}
</script>
You don't really need flags to maintain the state of the image's visibility. You can use classList's toggle method to toggle a class on/off or, in this case, visible/hidden, which makes things a little easier.
// Cache the elements, and add an event listener
// to the button
const img = document.querySelector('img');
const button = document.querySelector('button');
button.addEventListener('click', handleClick);
// Toggle the "hidden" class
function handleClick() {
img.classList.toggle('hidden');
}
.hidden { visibility: hidden; }
img { display: block; margin-bottom: 1em; }
button:hover { cursor: pointer; background-color: #fffff0; }
<img class="hidden" src="https://dummyimage.com/100x100/000/fff">
<button>Click</button>
Additional documentation
addEventListener
querySelector
Note: this will replace all the styles applied to 'melmanId'
<script>
let show = true;
function showHideM() {
show = !show;
if(show){
document.getElementById("melmanId").style.display = "inline";
}else{
document.getElementById("melmanId").style.display = "none";
}
}
</script>

Deduct Timer Count Every Page Refresh (Vanilla Javascript)

I am absolutely a noob when it comes to Javascript so I hope someone can help me please. I made a very simple Vanilla JS + HTML code that counts the number of times that it reaches 10 seconds (10 seconds = 1 count). This code will also refresh the page onmouseleave and when I change tab using window.onblur. My problem is that every time the page refreshes, the counter will go back to zero. What I want is that for the counter to deduct just one (or a specific number of) count every page refresh instead of completely restarting the count to zero. Please help me with Vanilla Javascript only and no JQuery (because I am planning to use this code personally and offline). Thank you in advance.
For those who may wonder what's this code is for, I want to create this to encourage myself to stay away from my computer for a certain period everyday. Like, if I can stay away from my computer for 100 counts, then I can use my computer freely after. I am addicted to the internet and I want to make this as my own personal way of building self-control.
Here is my code:
<style>
label {
color: orange;
}
p {
border-radius: 0px;
color: black;
text-decoration: none;
font-family: Consolas !important;
font-size: 16px;
font-weight: normal;
outline: none;
line-height: 0.25 ;
}
</style>
<body onmouseleave="window.location.reload(true)">
<p>You have earned <label id="pointscounter">00</label> point/s.</p>
<script>
var PointsLabel = document.getElementById("pointscounter");
var totalCountPoints = 0;
setInterval(setTimePoints, 10000);
function setTimePoints() {
++totalCountPoints;
PointsLabel.innerHTML = pad(totalCountPoints);
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
</script>
<script>
var blurred = false;
window.onblur = function() { blurred = true; };
window.onfocus = function() { blurred && (location.reload()); };
</script>
</body>
Storage
If you want the data to survive a reload, you need to save it somewhere. There are multiple options you can use. I used localStorage. You can learn more about it here: https://www.w3schools.com/jsref/prop_win_localstorage.asp. localStorage even survives closing the Browser Tab.
If you want to reset the data in a new session, you can use sessionStorage (just replace localStorage with sessionStorage): https://www.w3schools.com/jsref/prop_win_sessionstorage.asp.
What I did:
Save data on blur
If a blur-event occurs, the data is saved.
I also stopped the interval because there is no need for the interval anymore.
blurred variable
There is (currently?) no need for this variable.
The only usecase seems to be:
window.onfocus = function() {
blurred && location.reload();
};
To my knowledge you don't need this variable here.
Comming back
If the user already has points in localstorage, the current Points are calculated based on the points in localstorage. It currently deducts 1 point.
Using onmouseleave
I replaced the location.reload(true) on the body-tag with a function call. Everytime the mouse leaves, it calls this function. This function calls the onBlur function. The onBlur function is there, to ensure, that both window.onblur and onmouseleave do the same thing (save & stop). After the onBlur function is called, an EventListener is added to wait for mouseenter. When the mouse is seen again, we can reload the page with the onFocus function. It wouldn't reload the page as soon as the mouse left, because the timer would start (bc of reload), even if the mouse wasn't on the document.
Todo:
There is currently no check to see, if a the mouse in on the document after a reload. The timer will begin, even if the mouse isn't on the document.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
label {
color: orange;
}
p {
border-radius: 0px;
color: black;
text-decoration: none;
font-family: Consolas !important;
font-size: 16px;
font-weight: normal;
outline: none;
line-height: 0.25;
}
</style>
</head>
<body onmouseleave="mouseLeft()">
<p>You have earned <label id="pointscounter">00</label> point/s.</p>
<script>
var PointsLabel = document.getElementById("pointscounter");
var totalCountPoints = 0;
// Calculate Points if user has already collected points
if (localStorage.getItem("points") !== null) {
// You can change, how many points to deduct
const pointsToDeduct = 1;
var tempPoints = localStorage.getItem("points");
totalCountPoints = tempPoints - pointsToDeduct;
// Reset to 0 if points now negative
if (totalCountPoints < 0) {
totalCountPoints = 0;
}
PointsLabel.innerHTML = pad(totalCountPoints);
}
// need to save, to stop/clear it later
var timePointsInterval = setInterval(setTimePoints, 10000);
function setTimePoints() {
++totalCountPoints;
PointsLabel.innerHTML = pad(totalCountPoints);
}
function pad(val) {
var valString = val + "";
if (valString.length < 2) {
return "0" + valString;
} else {
return valString;
}
}
function mouseLeft() {
onBlur();
document.addEventListener("mouseenter", onFocus);
}
function onBlur() {
// save Current Points:
localStorage.setItem("points", totalCountPoints);
//stop the timer
clearInterval(timePointsInterval);
}
function onFocus() {
location.reload();
}
// Blur Detection
var blurred = false;
window.onblur = function () {
// [-] blurred = true;
onBlur();
};
window.onfocus = function () {
// [-] blurred && location.reload();
onFocus();
};
</script>
</body>
</html>

Detect drop in Drag & Drop (Adobe CC/HTML5/JavaScript)

I am trying to create a simple drag and drop activity in Adobe CC.
I have got the dragging function working fine, however I can't get it to detect when the dragged button hits the target.
Any help would be greatly appreciated!
Here is my code:
function Main()
{
createjs.Touch.enable(stage);//Emable Touch Gesture Recognition
createjs.Ticker.addEventListener("tick", this.update.bind(this));
this.addTargetsToButtons();//Add Button Actions
}
var phonemes = ["s","a","t","p","i","n","m","d","g","o","c","k","ck","e","u","r","h","b","f","ff","l","ll","ss"];
var draggablePhonemes = [this.b0, this.b1, this.b2];
var targets = [this.target1];
Main.prototype.addTargetsToButtons= function(){//Add Actions To All Our Buttons
for (i = 0; i<draggablePhonemes.length; i++){
console.log(draggablePhonemes[i]);
draggablePhonemes[i].addEventListener("pressmove", draggedStart.bind(this));
draggablePhonemes[i].addEventListener("pressup",draggedStop.bind(this));
draggablePhonemes[i].name = phonemes[i];
}
}
function draggedStart(event) {
console.log(event.currentTarget.name)
var p = stage.globalToLocal(event.stageX, event.stageY);
event.currentTarget.x = p.x;
event.currentTarget.y = p.y;
//The buttons can be dragged onto target1, target2, target3
}
function draggedStop(event) {
console.log(event.currentTarget.name + " Has Stopped Moving")
if (event.currentTarget.hitTest(targets[0].x,targets[0].y)){
console.log("Collision");
}
}
You need to add ondragover data attribute to the target and set it to this function:
function allowDrop(event) {
event.preventDefault();
console.log("Look, it hits the target");
}
By default, data/elements cannot be dropped in other elements. To allow that to happen, you must prevent the default handling of the element.
Note: You might also need to add ondrop data attribute to the target set to a function, so the actual drop can happen.
ex.
function drop(event) {
event.preventDefault();
var data = event.currentTarget.name;
event.target.appendChild(document.getElementById(data));
}
And this is what the html element should look like:
<div ondrop="drop(event)" ondragover="allowDrop(event)"></div>

script only firing on text link, not span

first post so go easy on my noobness!
I have a script that opens a popup window when some linked text is clicked, but now I want to get rid of the text and just use an empty span with background img as the link. Needless to say the script will not work with just the span and I'd appreciate some pointers on how to modify it to work (or suggest any workarounds if it ain't gonna work on an empty span).
Current link structure (which uses the text link to trigger the js window containing thelink.com):
<a href="http://thelink.com" class="pop">
<span class="icon_bg"><!-- empty span with image as background --></span>
some text here
</a>
Desired link structure (no text, just empty span with bg img):
<a href="http://thelink.com" class="pop">
<span class="icon_bg"><!-- empty span with image as background --></span>
</a>
Current script:
function popWin() {
function addEvent(element, eventName, callback) {
if (element.addEventListener) {
element.addEventListener(eventName, callback, false);
} else {
element.attachEvent("on" + eventName, callback);
}
}
function init() {
var links = document.querySelectorAll('a.pop');
    for (var i = 0; i < links.length; i++) {
popWin.addEvent(links[i], 'click', popWin.popup)
    }
}
function openPopup(e) {
var top = (screen.availHeight - 500) / 2;
var left = (screen.availWidth - 500) / 2;
var e = (e ? e : window.event);
    var target = (e.target ? e.target : e.srcElement);
var popup = window.open(
target.href,
'social',
'width=550,height=420,left='+ left +',top='+ top +',location=0,menubar=0,toolbar=0,status=0,scrollbars=1,resizable=1'
);
if(popup) {
popup.focus();
e.preventDefault();
return false;
}
return true;
}
return {
init: init,
popup: openPopup,
addEvent: addEvent
}}
var popWin = new popWin();
popWin.addEvent(window, 'load', popWin.init)
My hunch is to somehow define the span tag as the target using .nodeName. All help appreciated.
You can use the <span>&nbsp</span> to simulate invisible text and thus will cause the span to get width.
Your span is empty currently. Add some content to it, then it would work for you.
<span>Content</span>
Now the event would trigger for you.
Background image won't be visible until you have some width and height for the span. I would refer you to use
span {
/* change the display property of span; default is inline */
display: inline-block;
/* because width and height can be applied to only block level elements */
width: 100px;
height: 100px;
}
Now try it again. Have my fiddle, it makes a good use of your example, and it enables click on the textless span.
Also, you're not having any click handler in your code.
<span onclick="popWin()">
This would enable the click event.
http://jsfiddle.net/afzaal_ahmad_zeeshan/FdjZy/
OK, I think I've worked out the solution, but am curious if this is gonna be bombproof (seems to work in the few browsers I've tried so far).
Changed the line:
var target = (e.target ? e.target : e.srcElement)
To:
var target = (e.target ? e.target : e.srcElement).parentNode;
Now the span tag is triggering the script without the need for any text anywhere within the a tags.

Changing parent css on child focus (without breaking parent:hover css rules)

I made a menu on html (on the side and 100% heigth, expandeable as in android holo)
<div id="menu">
<button class="menubutton"></button>
<button class="menubutton"></button>
</div>
The menu normally remains transparent and with a short width:
#menu {
background-color: transparent;
width: 8%;
}
The idea was to expand and color it on hover. It was easy:
#menu:hover {
background-color: blue;
width: 90%;
}
There is no problem untill here. I need the same effect on focus. There is no way in css to change parent css on child focus (neither hover by the way, but it is not needed, cuase i can use the entire menu hover).
So i used a script:
var menubuttonfocus = document.getElementsByClassName("menubutton");
for (i=0; i<menubuttonfocus.length; i++) {
menubuttonfocus[i].addEventListener("focus", function() {
menu.style.backgroundColor = "blue";
menu.style.width = "90%";
});
menubuttonfocus[i].addEventListener("blur", function() {
menu.style.backgroundColor = "transparent";
menu.style.width = "8%";
});
}
The script works just fine, the problem is that when you trigger those events by focusing a button, the css of #menu:hover changes somehow and #menu does not change when hovering. I tried to solve this by doing something similar but with hover instead of focus:
menu.addEventListener("mouseenter", function(){
menu.style.backgroundColor = "blue";
menu.style.width = "90%";
});
menu.addEventListener("mouseout", function(){
menu.style.backgroundColor = "transparent";
menu.style.width = "8%";
});
This works somehow, but it is REALLY buggy.
I tried also to select "#menu:hover,#menu:focus", but it doesn't work because the focus is on the button elements and not in #menu.
Please avoid jquery if posible, and i know it's asking for too much but a pure css solution would be awesome.
Probably helpful info: html element are created dinamically with javascript.
I can show more code or screenshot, you can even download it (it is a chrome app) if needed: chrome webstore page
Thanks.
SOLVED: I did what #GCyrillus told me, changing #menu class on focus via javascript eventListener. .buttonbeingfocused contains the same css as "#menu:hover". Here is the script:
var menubuttonfocus = document.getElementsByClassName("menubutton");
for (i=0; i<menubuttonfocus.length; i++) {
menubuttonfocus[i].addEventListener("focus", function() {
menu.classList.add("buttonbeingfocused");
});
menubuttonfocus[i].addEventListener("blur", function() {
menu.classList.remove("buttonbeingfocused");
});
}
if the problem is what I think it is - you forgetting about one thing:
When you focusing / mouseentering the .menubutton - you are mouseleaving #menu and vice-versa - so your menu behaviour is unpredictible because you want to show your menu and hide it at the same time.
solution is usually setting some timeout before running "hiding" part of the script, and clearing this timeout (if exist) when running "showing" part.
it will be something like this:
var menuTimeout;
function showMenu() {
if (menuTimeout) clearTimeout(menuTimeout);
menu.style.backgroundColor = "blue";
menu.style.width = "90%";
}
function hideMenu() {
menuTimeout = setTimeout( function() {
menu.style.backgroundColor = "transparent";
menu.style.width = "8%";
}, 800);
}
//then add your listeners like you did - but put these functions as a handlers - like this:
menu.addEventListener("mouseenter", showMenu);
...
//in addition you need also "mouseenter" and "mouseleave" events handled on .menubuttons

Categories