Hovering over pseudo after element to change the style - javascript

I'm trying to create a simple demo where rolling over a pseudo element will change the style of its parent. In other words, I want to be able to roll over the letter e in the top right corner of the image and then display the text content.
I've managed to get it working when rolling over the image itself, but not the pseudo element. I've commented out the working code for rolling over the image itself, and left the incorrect pseudo rollover code uncommented out.
I wonder whether you can actually select pseudo elements in JS as it shows null when trying to select any pseudo element.
Any ideas would be appreciated. Thanks for any help. The code is below:
Codepen: https://codepen.io/anon/pen/NZvdzr
/*document.querySelector('#img-wrap').onmouseover = function() {
document.querySelector('#caption-wrap').style.opacity = 1;
}
document.querySelector('#img-wrap').onmouseout = function() {
document.querySelector('#caption-wrap').style.opacity = 0;
}*/
document.querySelector('#img-wrap:after').onmouseover = function() {
document.querySelector('#caption-wrap').style.opacity = 1;
}
document.querySelector('#img-wrap:after').onmouseout = function() {
document.querySelector('#caption-wrap').style.opacity = 0;
}
#img-wrap {
width: 30%;
position: relative;
}
#caption-wrap {
position: absolute;
top: 0;
right: 0;
opacity: 0;
}
img {
width: 100%;
}
#img-wrap:after {
content: 'e';
position: absolute;
top: 0;
right: 0;
}
<div id='img-wrap'>
<img src='https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ-bCnPPMYp6QIfrCr2BR-imm_Sw9IHCXIXzE5fei7R8PTBKYGd'>
<div id='caption-wrap'>
<p>some text will appear</p>
</div>
</div>

You cannot listen to pseudo elements, but you can find out some interesting info via window.getComputedStyle(). Below is a demo.
I'm listening to mouse move on the image element and comparing the coords to see if they fall between the rectangle of the pseudo element.
There is a padding of 2px on each tolerance, you could change that to something else if you want to be more forgiving with the mouse over detection.
CanIUse.com says window.getComputedStyle() is supported by all browsers, but I haven't tested if they all return the proper coordinate information for this to work – you should cross browser test this before using.
var element = document.querySelector('#img-wrap')
element.onmousemove = function(event){
var elementRect = element.getBoundingClientRect()
var pseudo = window.getComputedStyle(element, ':after')
var pseudoRect = {
top: parseFloat(pseudo.top),
left: parseFloat(pseudo.left),
width: parseFloat(pseudo.width),
height: parseFloat(pseudo.height),
}
var mouseX = event.clientX
var mouseY = event.clientY
var yTolTop = elementRect.top + pseudoRect.top - 2
var yTolBot = elementRect.top + pseudoRect.top + pseudoRect.height + 2
var xTolLeft = elementRect.left + pseudoRect.left - 2
var xTolRight = elementRect.left + pseudoRect.left + pseudoRect.width + 2
//console.log(elementRect.top, yTolTop, mouseY, yTolBot, " | ", elementRect.left, xTolLeft, mouseX, xTolRight)
if(mouseY > yTolTop && mouseY < yTolBot && mouseX > xTolLeft && mouseX < xTolRight){
document.querySelector('#caption-wrap').style.opacity = 1;
}else{
document.querySelector('#caption-wrap').style.opacity = 0;
}
}
element.onmouseout = function(){
document.querySelector('#caption-wrap').style.opacity = 0;
}
#img-wrap {
width: 30%;
position: relative;
}
#caption-wrap {
position: absolute;
top: 0;
right: 0;
opacity: 0;
}
img {
width: 100%;
}
#img-wrap:after {
content: 'e';
position: absolute;
top: 0;
right: 0;
}
<div id='img-wrap'>
<img src='https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ-bCnPPMYp6QIfrCr2BR-imm_Sw9IHCXIXzE5fei7R8PTBKYGd'>
<div id='caption-wrap'>
<p>some text will appear</p>
</div>
</div>
Codepen: https://codepen.io/bergy/pen/YoxZBp
(edit: since the JS was getting the rects outside of the mouse move function, if the element was ever moved it would stop working. Now it looks for rects in mouse move so the bug is fixed)

Related

Lit Web Component - Trying to select element

I am trying to transform this element into a standard web component using Lit. (https://www.w3schools.com/howto/howto_js_image_comparison.asp)
I totally new to Lit and to web components and am struggling to select elements from the shadow DOM. Right now, I am stuck with the var x inside the initComparisons() function. I am aware that the document object does not exist in the shadow dom and must be replaced by renderRoot, however, I am not sure either If I am selecting the elements the right way or what does replace the window object... Do you notice something wrong with this code? I am stuck at the first lines of the initComparisons() function as x always returns null no matter what I do....
Any help will be appreciated.
Thank you very much.
import {
LitElement,
css,
html,
} from "https://cdn.jsdelivr.net/gh/lit/dist#2/all/lit-all.min.js";
export class Comparator extends LitElement {
static properties = {
baseImage: "",
imageWidth: "",
imageHeight: "",
altImage: "",
};
// Define scoped styles right with your component, in plain CSS
static styles = css`
* {
box-sizing: border-box;
}
.img-comp-container {
position: relative;
height: 200px; /*should be the same height as the images*/
}
.img-comp-img {
position: absolute;
width: auto;
height: auto;
overflow: hidden;
}
.img-comp-img img {
display: block;
vertical-align: middle;
}
.img-comp-slider {
position: absolute;
z-index: 11;
cursor: ew-resize;
/*set the appearance of the slider:*/
width: 40px;
height: 40px;
background-color: #2196f3;
opacity: 0.7;
border-radius: 50%;
}
.border-slider {
position: absolute;
z-index: 10;
cursor: ew-resize;
/*set the appearance of the slider:*/
width: 5px;
height: 130%;
background-color: red;
opacity: 1;
}
.border-slider::after {
content: url("./separator.svg");
position: absolute;
width: 30px;
height: 30px;
background: red;
top: calc(50% - 15px);
left: calc(50% - 15px);
}
`;
constructor() {
super();
// Declare reactive defaults
this.baseImage = "https://api.lorem.space/image/house?w=800&h=600";
this.altImage = "https://api.lorem.space/image/house?w=800&h=600";
this.imageWidth = "800px";
this.imageHeight = "600px";
}
connectedCallback() {
super.connectedCallback();
this.initComparisons();
}
// Render the UI as a function of component state
render() {
return html`
<div class="img-comp-container">
<div class="img-comp-img">
<img
src="${this.baseImage}"
width="${this.imageWidth}"
height="${this.imageHeight}"
/>
</div>
<div id="img-comp-overlay" class="img-comp-img">
<img
src="${this.altImage}"
width="${this.imageWidth}"
height="${this.imageHeight}"
/>
</div>
</div>
`;
}
//HELPER FUCTIONS GO HERE
initComparisons() {
var x, i;
/*find all elements with an "overlay" class:*/
x = this.renderRoot.querySelector("#img-comp-overlay");
for (i = 0; i < x.length; i++) {
/*once for each "overlay" element:
pass the "overlay" element as a parameter when executing the compareImages function:*/
compareImages(x[i]);
}
function compareImages(img) {
var slider,
img,
clicked = 0,
w,
h;
/*get the width and height of the img element*/
w = img.offsetWidth;
h = img.offsetHeight;
/*set the width of the img element to 50%:*/
img.style.width = w / 2 + "px";
/*create slider:*/
slider = this.renderRoot.createElement("DIV");
slider.setAttribute("class", "border-slider");
/*insert slider*/
img.parentElement.insertBefore(slider, img);
/*position the slider in the middle:*/
slider.style.top = h / 2 - slider.offsetHeight / 2 + "px";
slider.style.left = w / 2 - slider.offsetWidth / 2 + "px";
/*execute a function when the mouse button is pressed:*/
slider.addEventListener("mousedown", slideReady);
/*and another function when the mouse button is released:*/
this.renderRoot.addEventListener("mouseup", slideFinish);
/*or touched (for touch screens:*/
slider.addEventListener("touchstart", slideReady);
/*and released (for touch screens:*/
window.addEventListener("touchend", slideFinish);
function slideReady(e) {
/*prevent any other actions that may occur when moving over the image:*/
e.preventDefault();
/*the slider is now clicked and ready to move:*/
clicked = 1;
/*execute a function when the slider is moved:*/
window.addEventListener("mousemove", slideMove);
window.addEventListener("touchmove", slideMove);
}
function slideFinish() {
/*the slider is no longer clicked:*/
clicked = 0;
}
function slideMove(e) {
var pos;
/*if the slider is no longer clicked, exit this function:*/
if (clicked == 0) return false;
/*get the cursor's x position:*/
pos = getCursorPos(e);
/*prevent the slider from being positioned outside the image:*/
if (pos < 0) pos = 0;
if (pos > w) pos = w;
/*execute a function that will resize the overlay image according to the cursor:*/
slide(pos);
}
function getCursorPos(e) {
var a,
x = 0;
e = e.changedTouches ? e.changedTouches[0] : e;
/*get the x positions of the image:*/
a = img.getBoundingClientRect();
/*calculate the cursor's x coordinate, relative to the image:*/
x = e.pageX - a.left;
/*consider any page scrolling:*/
x = x - window.pageXOffset;
return x;
}
function slide(x) {
/*resize the image:*/
img.style.width = x + "px";
/*position the slider:*/
slider.style.left = img.offsetWidth - slider.offsetWidth / 2 + "px";
}
}
}
}
customElements.define("image-compare", Comparator);
connectedCallback() is likely too early to call this.initComparison() as the elements might not have been populated within the shadowroot. That happens on first render so you can grab those elements in firstUpdated() instead.

How to make custom follow cursor follow Y axis scroll

I'm using a bit of HTML & CSS on my squarespace site to create a custom follow cursor. I want to just have a floaty circle with no actual cursor displayed. I've gotten it to mostly work, but when my site scrolls the follow cursor doesn't move with the page scroll and just gets stuck at the top.
And that just caused the follow cursor to stop moving with mouse movement entirely, becoming static on the center of the page.
Injecting HTML & CSS on to squarespace site to create a custom follow cursor:
body {
background: #161616;
}
.wrap {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: hidden;
}
#ball {
width: 60px;
height: 60px;
background: none;
border: 1px solid grey;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
margin: -10px 0 0 -10px;
pointer-events: none;
}
<body onload="followMouse();">
<div class="wrap">
<div id="ball"></div>
</div>
<script type="text/javascript">
var $ = document.querySelector.bind(document);
var $on = document.addEventListener.bind(document);
var xmouse, ymouse;
$on('mousemove', function (e) {
xmouse = e.clientX || e.pageX;
ymouse = e.clientY || e.pageY;
});
var ball = $('#ball');
var x = void 0,
y = void 0,
dx = void 0,
dy = void 0,
tx = 0,
ty = 0,
key = -1;
var followMouse = function followMouse() {
key = requestAnimationFrame(followMouse);
if(!x || !y) {
x = xmouse;
y = ymouse;
} else {
dx = (xmouse - x) * 0.125;
dy = (ymouse - y) * 0.125;
if(Math.abs(dx) + Math.abs(dy) < 0.1) {
x = xmouse;
y = ymouse;
} else {
x += dx;
y += dy;
}
}
ball.style.left = x + 'px';
ball.style.top = y + 'px';
};
</script>
</body>
[EDIT] Great job on updating your question, the demo and the problem are very clear now. Don't worry about your demo not scrolling, I just added a bunch of divs with some height in my demo to simulate that. Here's everything you need to / should change to make it all work:
var followMouse = function followMouse() ... is very strange syntax and I'm not sure what the exact outcome will be.
Either declare the function normally function followMouse() ..., or store it in a variable using either the:
function definition var followMouse = function() ... or
arrow definition var followMouse = () => ...
To simply get it all working you just need to adjust for the current scroll amount of either the document or in my demo's case the element with class ".wrap".
This can be done using the scrollTop member of the object returned by your $() function.
I started by just adding $(".wrap").scrollTop to the ymouse variable in the mousemove listener, but while this works it needs you to move the mouse for the circle to realize it's scrolled off the page.
So instead we just add $(".wrap").scrollTop to the css that is being set to the ball in the last lines of followMouse.
I changed the overflow property from hidden to scroll since that's kind of where the problem is occuring ;)
I've also added cursor: none to your .wrap css so that you get the desired effect of no cursor but your custom one.
var $ = document.querySelector.bind(document);
var $on = document.addEventListener.bind(document);
var followMouse = function() {
key = requestAnimationFrame(followMouse);
if (!x || !y) {
x = xmouse;
y = ymouse;
} else {
dx = (xmouse - x) * 0.125;
dy = (ymouse - y) * 0.125;
if (Math.abs(dx) + Math.abs(dy) < 0.1) {
x = xmouse;
y = ymouse;
} else {
x += dx;
y += dy;
}
}
ball.style.left = x + 'px';
ball.style.top = $(".wrap").scrollTop + y + 'px';
};
var xmouse, ymouse;
var ball = $('#ball');
var x = void 0,
y = void 0,
dx = void 0,
dy = void 0,
tx = 0,
ty = 0,
key = -1;
$on('mousemove', function(e) {
xmouse = e.clientX || e.pageX;
ymouse = e.clientY || e.pageY;
});
body {
background: #161616;
}
.wrap {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
overflow: scroll;
cursor: none;
}
#ball {
width: 60px;
height: 60px;
background: none;
border: 1px solid grey;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
margin: -10px 0 0 -10px;
pointer-events: none;
}
.makeOverflow {
width: 100px;
height: 300px;
}
<body onload="followMouse();">
<div class="wrap">
<div id="ball"></div>
<div class="makeOverflow"> </div>
<div class="makeOverflow"> </div>
<div class="makeOverflow"> </div>
<div class="makeOverflow"> </div>
</div>
</body>
This will probably be fixed by changing the #ball CSS from being absolutely positioned to a fixed position, then it should scroll down the page with your original js

jQuery offset issues with div overflow: scroll

I am writing an HTML5 board game and I am having issues with jQuery's offset(). The grid of DIVs that make up the game board reside within a wrapper DIV that has CSS that sets overflow:scroll/width and height:100%. The game board itself is quite larger, so the scroll can be quite a bit horizontally and vertically.
The problem is that when I click on a DIV and try to move the player to that board piece, the player shifts around and is never in a consistent place relative to the board piece clicked.
Code:
$(".boardGridPiece").click(function(){
if(!$(this).hasClass("room") && $(this).hasClass("eligibleMove")){
playerStartX = $(this).offset().left;
playerStartY = ($(this).offset().top;
player.css("left", playerStartX);
player.css("top", playerStartY);
determineEligibleMoves($(this).attr("id"));
}
});
You can see that when a board piece is clicked, the offset of the board piece is grabbed and set to the player's X and Y.
CSS:
#boardWrapper {
position:relative;
width:100%;
height:80%;
overflow: scroll;
}
#theGame {
background-color: #fff;
height: 1080px;
width: 1920px;
}
Depending on where the player is relative to the current scroll view, when I click on a board piece he shifts around in a very inconsistent manner. Sometimes he's far left of where I click, or far up, etc.
What am I doing wrong? How do I take into account relative scroll position to get consistent positioning?
Here's a board for you to play with as an example. Also, my stab at the jquery is included as well. Basically, it finds where you clicked, calculated the px distance in float form, and animates the soldier to slide to his new position:
var black = '<td style="background-color:black"><div class="boardGridPiece"></div></td>';
var white = '<td style="background-color:white"><div class="boardGridPiece"></div></td>';
var squares = [black, white];
var grid = "";
for (var i = 0; i < 9; i++) {
grid += "<tr>";
for (var j = 0; j < 16; j++) {
grid += squares[(i + j) % 2];
}
grid += "<\tr>";
}
$('#gameboard').append(grid);
var gridSelected = $('#gameboard').find('tr:nth-child(2)').find('td:first').find('div');
gridSelected.toggleClass('position');
$('.boardGridPiece').click(function () {
$('.position').removeClass('position');
var gridSelected = $(this);
gridSelected.toggleClass('position');
var thisBox = $('.position');
var finalX = 0;
var finalY = 0;
for (var i = 0; i < 9.00; i++) {
for (var j = 0; j < 16.00; j++) {
var aBox = $('#gameboard').find('tr:nth-child(' + (i + 1) + ')').find('td:nth-child(' + (j + 1) + ')').find('div');
if (thisBox.get(0) == aBox.get(0)) {
finalX = j + 1;
finalY = i;
i = j = 16; // soft break
}
}
}
var overX = (finalX * parseFloat(1920))/16.00;
var downY = (finalY * parseFloat(1080))/9.00;
$('#player').animate({ left: overX, top: downY });});
html {
background-color:gray;
}
#gameboard {
margin: 100px;
height:1080px;
width:1920px;
border:1px solid black;
}
.boardGridPiece {
height: 110px;
width: 110px;
}
.position {
box-sizing:border-box;
-moz-box-sizing:border-box;
-webkit-box-sizing:border-box;
border: 1px solid red;
}
#player {
position: absolute;
top: 120px;
left: 125px;
height: 200px;
width: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="gameboard" style=""></table>
<img id="player" src="http://img2.wikia.nocookie.net/__cb20110819034426/halo/images/7/74/ODST_Helljumper.png" />

Using two of the same javascripts

I've been trying to create a page with several before and after images (Using a slider to swap between the two).
However when I add the second piece of JavaScript code, it breaks the page. Even if I try to amend the (var) code to be unique from the previous script
In all honesty I don't quite understand what the JavaScript is doing which is why I'm probably unable to Google the solution. Any help would be appreciated, if you could try to explain in as much detail what I need to do and explain any specific terms that would help me develop further.
You can see all my code on the link (and below): http://codepen.io/sn0wm0nkey/pen/DakbA
var inkbox = document.getElementById("inked-painted");
var colorbox = document.getElementById("colored");
var fillerImage = document.getElementById("inked");
inkbox.addEventListener("mousemove",trackLocation,false);
inkbox.addEventListener("touchstart",trackLocation,false);
inkbox.addEventListener("touchmove",trackLocation,false);
function trackLocation(e)
{
var rect = inked.getBoundingClientRect();
var position = ((e.pageX - rect.left) / inked.offsetWidth)*100;
if (position <= 100) { colorbox.style.width = position+"%"; }
}
/* -----second JavaScript code---- */
var inkbox1 = document.getElementById("inked1-painted");
var colorbox1 = document.getElementById("colored1");
var fillerImage1 = document.getElementById("inked1");
inkbox1.addEventListener("mousemove",trackLocation,false);
inkbox1.addEventListener("touchstart",trackLocation,false);
inkbox1.addEventListener("touchmove",trackLocation,false);
function trackLocation(e1)
{
var rect1 = inked.getBoundingClientRect();
var position1 = ((e1.pageX - rect1.left) / inked1.offsetWidth)*100;
if (position1 <= 100) { colorbox1.style.width = position1+"%"; }
}
body { background: #113; }
div#inked-painted {
position: relative; font-size: 0;
-ms-touch-action: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
}
div#inked-painted img {
width: 100%; height: auto;
}
div#colored {
background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/colored-panel.jpg);
position: absolute;
top: 0; left: 0; height: 100%;
width: 50%;
background-size: cover;
}
div#inked-painted:hover {
cursor: col-resize;
}
/*-------- second css sheet --------- */
div#inked1-painted {
position: relative; font-size: 0;
-ms-touch-action: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
}
div#inked1-painted img {
width: 100%; height: auto;
}
div#colored1 {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 50%;
background-size: cover;
background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/colored-panel.jpg);
}
div#inked1-painted:hover {
cursor: col-resize;
}
<Div>
<div id="inked-painted">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/inked-panel.png" id="inked" alt>
<div id="colored"></div>
</div>
<p></p>
<div>
<div id="inked1-painted">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/inked-panel.png" id="inked1" alt>
<div id="colored1"></div>
</div>
Howard's solution works but can be improved even more to remove duplication.
Use classes instead of Ids
Find the elements inside the div that is receiving the mousemove instead of passing them in
Don't duplicate the CSS
function onMouseMove(e) {
var inked = this.getElementsByTagName("img")[0];
var colorbox = this.querySelector('.colored');
var rect = inked.getBoundingClientRect();
var position = ((e.pageX - rect.left) / inked.offsetWidth) * 100;
if (position <= 100) {
colorbox.style.width = position + "%";
}
}
function trackLocation(el) {
el.addEventListener("mousemove", onMouseMove, false);
el.addEventListener("touchstart", onMouseMove, false);
el.addEventListener("touchmove", onMouseMove, false);
}
var wrappers = document.querySelectorAll('.inked-painted');
for (var i = 0; i < wrappers.length; i++) {
trackLocation(wrappers[i]);
}
div.inked-painted {
position: relative;
font-size: 0;
-ms-touch-action: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
}
div.inked-painted img {
width: 100%;
height: auto;
}
.colored {
background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/colored-panel.jpg);
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 50%;
background-size: cover;
}
div.inked-painted:hover {
cursor: col-resize;
}
<div class="inked-painted">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/inked-panel.png" />
<div class="colored"></div>
</div>
<p></p>
<div class="inked-painted">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4273/inked-panel.png" />
<div class="colored"></div>
</div>
First, Java != JavaScript. They are two very different languages.
Second, your issue is that your second function is named the same as your first function. The second one essentially overwrites the first one, so the first doesn't exist any longer. Simply use a different name for your second function, and your code works just fine.
However, it would be better to find a way to reuse your first function, instead of having two almost identical functions.
Here is what you are doing with your JavaScript how it is currently written.
Declare and assign variables inkbox, colorbox, fillerImage
Add event handlers
Create a function in the global scope by the name of trackLocation
Declare and assign variables inkbox1, colorbox1, fillerImage1
Add event handlers
Overwrite the trackLocation function in the global scope
All of this is being done synchronously, just as I have it listed here. So, when an event fires on inkbox, it calls the new function that overwrote the original.
Another problem that I see (unless you omitted some code) is you have some variables that are not defined, which will cause a problem within your function.
function trackLocation (e) {
// inked is undefined
var rect = inked.getBoundingClientRect();
// inked is undefined
var position = ((e.pageX - rect.left) / inked.offsetWidth)*100;
if (position <= 100) { colorbox.style.width = position+"%"; }
}
You'll need to rewrite your function to accept local variables like this:
function trackLocation (e, inked, colorbox) {
var rect = inked.getBoundingClientRect();
var position = ((e.pageX - rect.left) / inked.offsetWidth)*100;
if (position <= 100) { colorbox.style.width = position+"%"; }
}
Now this one function can be reused in all of your event handlers, like this:
function trackLocation (e, inked, colorbox) {
var rect = inked.getBoundingClientRect();
var position = ((e.pageX - rect.left) / inked.offsetWidth)*100;
if (position <= 100) { colorbox.style.width = position+"%"; }
}
var inkbox = document.getElementById("inked-painted");
var colorbox = document.getElementById("colored");
var fillerImage = document.getElementById("inked");
inkbox.addEventListener("mousemove", function (e) { trackLocation(e, inkbox, colorbox); });
inkbox.addEventListener("touchstart", function (e) { trackLocation(e, inkbox, colorbox); });
inkbox.addEventListener("touchmove", function (e) { trackLocation(e, inkbox, colorbox); });
var inkbox1 = document.getElementById("inked1-painted");
var colorbox1 = document.getElementById("colored1");
var fillerImage1 = document.getElementById("inked1");
inkbox1.addEventListener("mousemove", function (e) { trackLocation(e, inkbox1, colorbox1); });
inkbox1.addEventListener("touchstart", function (e) { trackLocation(e, inkbox1, colorbox1); });
inkbox1.addEventListener("touchmove", function (e) { trackLocation(e, inkbox1, colorbox1); });

Draggable Columns With Pure JavaScript

I'm trying to build a draggable column based layout in JavaScript and having a bit of hard time with it.
The layout comprises of 3 columns (divs), with two dragable divs splitting each. The idea is that they are positioned absolutely and as you drag the draggers, the columns' respective widths, and left values are updated.
The three columns should always span the full width of the browser (the right most column is 100% width), but the other two should remain static by default when the browser is resized (which is why i'm using px, not %).
My code isn't working as of yet, I'm relatively new to JavaScript (which is why I don't want to use jQuery).
Having said that, there must be a more efficient (and cleaner) way of achieving this with less code that works (without reaching for the $ key).
If anyone with some awesome JS skills can help me out on this I'd be super-appreciative.
Here's the fiddle I'm working on http://jsfiddle.net/ZFwz5/3/
And here's the code:
HTML
<!-- colums -->
<div class="col colA"></div>
<div class="col colB"></div>
<div class="col colC"></div>
<!-- draggers -->
<div class="drag dragA" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left:100px;"><div></div></div>
<div class="drag dragB" style="position: absolute; width: 0px; height: 100%; cursor: col-resize; left: 300px;"><div></div></div>
CSS:
body {
overflow:hidden;
}
.col {
position: absolute;
height:100%;
left: 0;
top: 0;
overflow: hidden;
}
.colA {background:red;width:100px;}
.colB {background:green; width:200px; left:100px;}
.colC {background:blue; width:100%; left:300px;}
.drag > div {
background: 0 0;
position: absolute;
width: 10px;
height: 100%;
cursor: col-resize;
left: -5px;
}
and my terrible JavaScript:
//variabe columns
var colA = document.querySelector('.colA');
var colB = document.querySelector('.colB');
var colC = document.querySelector('.colC');
//variable draggers
var draggers = document.querySelectorAll('.drag');
var dragA = document.querySelector(".dragA");
var dragB = document.querySelector(".dragB");
var dragging = false;
function drag() {
var dragLoop;
var t = this;
var max;
var min;
if (dragging = true) {
if (this == dragA) {
min = 0;
max = dragB.style.left;
} else {
min = dragA.style.left;
max = window.innerWidth;
}
dragLoop = setInterval(function () {
var mouseX = event.clientX;
var mouseY = event.clientY;
if (mouseX >= max) {
mouseX = max;
}
if (mouseY <= min) {
mouseY = min;
}
t.style.left = mouseX;
updateLayout();
}, 200);
}
}
function updateLayout() {
var posA = dragA.style.left;
var posB = dragB.style.left;
colB.style.paddingRight = 0;
colA.style.width = posA;
colB.style.left = posA;
colB.style.width = posB - posA;
colC.style.left = posB;
colC.style.width = window.innerWidth - posB;
}
for (var i = 0; i < draggers.length; i++) {
draggers[i].addEventListener('mousedown', function () {
dragging = true;
});
draggers[i].addEventListener('mouseup', function () {
clearInterval(dragLoop);
dragging = false;
});
draggers[i].addEventListener('mouseMove', function () {
updateLayout();
drag();
});
}
I see a couple of things wrong here. First of all, the mousemove event only fires on an element when the mouse is over that element. You might have better luck registering a mousemove listener on the parent of your div.drag elements, then calculating the mouse's position inside that parent whenever a mouse event happens, then using that position to resize your columns and your draggers.
Second, I'm not quite sure what you're trying to do by registering a function with setInterval. You're doing pretty well with registering event listeners; why not continue to use them to change the state of your DOM? Why switch to a polling-based mechanism? (and the function you pass to setInterval won't work anyway - it refers to a variable named event, which in that context is undefined.)
This is just a little example... I hope it can help you :)
window.onload = function() {
var myDiv = document.getElementById('myDiv');
function show_coords(){
var monitor = document.getElementById('monitor');
var x = event.clientX - myDiv.clientWidth / 2;
var y = event.clientY - myDiv.clientWidth / 2;
monitor.innerText = "X: " + x + "\n" + "Y: " + y;
myDiv.style.left = x + "px";
myDiv.style.top = y + "px";
}
document.onmousemove = function(){
if(myDiv.innerText == "YES"){show_coords();}
}
myDiv.onmousedown = function(){
myDiv.innerText = "YES";
}
myDiv.onmouseup = function(){
myDiv.innerText = "NO";
}
}

Categories