Is it possible to change the autoscroll offset when dragging? - javascript

I have a single-page app written in Vue.js. The UI comprises three main elements:
a navbar with position: fixed at the top and z-index: 2
a sidebar with position: fixed on the left and z-index: 1 and padded to account for the presence of the navbar
a central area, padded to account for the presence of the other fixed elements.
The sidebar is scrollable with overflow-y: auto. Some elements in the sidebar, that I designated here with the text item X, are draggable.
Normally, when dragging towards the edges of a scrollable area, the area will automatically scroll in that direction. In my application the sidebar does not auto-scroll, because the upper edge is hidden by the navbar.
An obvious solution would be to change the structure of the sidebar so that its upper edge lies visible instead of being one top-padded div. However this might break other things.
Question:
I would like to know if there's a way to trigger auto-scroll on drag when the mouse is arbitrarily close to the edge, e.g. $navbarHeight pixels.
I'm looking for a pure HTML5 solution, vanilla js, or something with my current framework (Vue.js v3).
What I already tried:
I've done a great deal of googling but I'm probably using the wrong search queries: "auto scroll while dragging", "scroll sensitivity area", "autoscroll offset", and several combinations of quotes did not yield relevant results.
JS Fiddle: https://jsfiddle.net/7f5wh1mj/4/
What the page looks like (see the fiddle for a working example):

Nowdays, you may use grid to create such layouts , here is an example without position but grid ;)
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript",
done: false
},
{
text: "Learn Vue",
done: false
},
{
text: "Play around in JSFiddle",
done: true
},
{
text: "Build something awesome",
done: true
}
],
items: 12
},
methods: {
toggle: function(todo) {
todo.done = !todo.done
}
}
})
* {
box-sizing: border-box;
/* reset , optionnal */
}
body {
background: #20262E;
font-family: Helvetica;
overflow: hidden;
margin: 0;
height: 100vh;
/* needed */
width: 100vw;
}
#app {
background: #fff;
display: grid;
grid-template-columns: 150px 1fr;
grid-template-rows: auto 1fr;
height: 100%;
/* do not remove */
}
.navbar {
grid-row: 1;
grid-column: 1 / 3;
padding: 0.5em;
background-color: lightgreen;
}
.sidebar {
background-color: lightgray;
/* z-index: 1; no need */
overflow-y: auto;
}
.content {
background-color: #fff;
/* z-index: 0; no need */
overflow: auto;
/* make it scroll too instead body */
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
aside p {
text-align: center;
color: white;
background:gray;
position:sticky;
top:0;
margin:0;
padding:0.25em 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="navbar">
<h1>Navbar</h1>
</div>
<aside class="sidebar">
<p>Sidebar</p>
<ul>
<li draggable v-for="n in items">item {{ n }}</li>
</ul>
</aside>
<div class="content">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<input type="checkbox" v-on:change="toggle(todo)" v-bind:checked="todo.done">
<del v-if="todo.done">
{{ todo.text }}
</del>
<span v-else>
{{ todo.text }}
</span>
</label>
</li>
</ol>
</div>
</div>
edit : added comment and a sticky example inside the sidebar.

Related

Showing a dropdown on click instead of on hover

I am using Avada (fusion-theme) theme on Wordpress for my website (under construction).
They have a mega-menu option that I am using, but I would want it to appear when someone clicks on the main-menu item instead of hovering over it.
Site: www.paradigmtek.com
So right now if someone hovers over say "smart home" at the top, the sub menu appears (smart home tech support, smart hub or speaker setup, etc.). But I would like it to appear on click instead of on hover.
I don't think this will require not a simple CSS trick, but a JS one.
Anyone has experience with that theme or know how to do it?
You can simply add a class to change the opacity of the dropdown menu upon clicking one of the menus. In this example below, I'm adding show class to dropdown to change opacity from 0 to 1 upon clicking the menu. At the same time, I'm addding a class to the clicked menu (i.e. clicked) to give it an accent colour to indicate that it is the menu being clicked.
const menus = document.querySelectorAll('.menu')
const dropdown = document.querySelector('.dropdown')
let activeMenu = null
menus.forEach(menu => {
menu.addEventListener('click', e => {
// Removing previous active menu that is not itself
if (activeMenu && activeMenu !== menu) {
activeMenu.classList.remove('clicked')
activeMenu = menu
}
else if (activeMenu && activeMenu === menu) {
activeMenu = null
} else {
activeMenu = menu
}
menu.classList.toggle('clicked')
// If there is an active menu, show
if (activeMenu) dropdown.classList.add('show')
else dropdown.classList.remove('show')
})
})
* {
box-sizing: border-box;
font-family: Helvetica;
}
html,
body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.container {
display: flex;
justify-content: center;
background: #121212;
}
.menu {
color: white;
margin: 20px;
padding: 20px;
border-radius: 5px;
cursor: pointer;
}
.menu:hover {
color: #ff8888;
}
.menu.clicked {
color: #ff8888;
}
.dropdown {
width: 100%;
height: 100px;
background: #333333;
display: flex;
flex-direction: column;
opacity: 0;
transition: opacity 0.5s ease;
}
.dropdown.show {
opacity: 1;
}
.line {
width: 100%;
height: 3px;
background: #00a5ff;
}
<div class="container">
<div class="menu">Menu 1</div>
<div class="menu">Menu 2</div>
<div class="menu">Menu 3</div>
<div class="menu">Menu 4</div>
</div>
<div class="dropdown">
<div class="line"></div>
</div>
In the options: avada options -> menu -> submenu
You just have to specify hover / click with the select button.
You can also build your own menu by creating a layout
-> avada -> layout
In your layout you can add a menu element on which you apply the wanted options.
Getting Started With Avada Layouts
Understanding Custom Headers
How To Use The Menu Element

How to make Fixed navbar with vue js?

I try build a landing page using vue.js, with header design like on the picture above.
So, I create a component called "header",with contain content according to the design.
How do I make a fixed navbar, when the page is scrolled the navbar is still on top ?
Another option could be to use the bootstrap-vue package.
It has the b-navbar component which can be made fixed to the
top
<b-navbar class="header" fixed="top"></b-navbar>
Example:
const vm = new Vue({el: '#app'})
<link href="http://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" rel="stylesheet"/><link href="http://unpkg.com/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"/><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><script src="http://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script><script src="http://unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script><div id="app">
<!-- ************************************ -->
<!-- Place the fixed prop within b-navbar -->
<!-- ************************************ -->
<b-navbar class="header" type="dark" variant="info" fixed="top">
<b-navbar-brand href="#"> My fixed header </b-navbar-brand>
</b-navbar>
<!-- *********************************** -->
<div style="margin-top: 60px;"><ol><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li><li>link</li></ol></div></div>
You can set a fixed navbar by applying the following class.
.header {
position:fixed; /* fixing the position takes it out of html flow - knows
nothing about where to locate itself except by browser
coordinates */
left:0; /* top left corner should start at leftmost spot */
top:0; /* top left corner should start at topmost spot */
width:100vw; /* take up the full browser width */
z-index:200; /* high z index so other content scrolls underneath */
height:100px; /* define height for content */
}
An element with position:fixed; property doesn't change when the window is scrolled, so a fixed positioned element will stay right.
new Vue({
el: "#app",
data:{
active: false
},
methods: {
toggleNavClass(){
if(this.active == false){
return 'nav'
} else {
return 'sticky-nav'
}
}
},
mounted(){
window.document.onscroll = () => {
let navBar = document.getElementById('nav');
if(window.scrollY > navBar.offsetTop){
this.active = true;
} else {
this.active = false;
}
}
}
})
/*scrollY returns the scroll amount in pixels.
offsetTop is the px difference between the navBar and closest parent element*/
body {
margin: 0;
box-sizing: border-box;
}
#app {
color: #2c3e50;
background-color: #ccd6dd;
height: 120vh;
}
a {
font-weight: bold;
color: white;
text-decoration: none;
margin: 0 1vw;
}
a:hover {
transition: linear 100ms;
color: red;
}
/* two classes, decided on scroll */
.nav {
transition: 100ms;
padding: 25px;
}
.sticky-nav{
transition: 100ms;
padding: 20px;
}
#nav {
display: flex;
justify-content: space-between;
width: 100%;
background-color: #55acee;
position: fixed;
top: 0;
}
/* have to add the ID nav (#nav) otherwise the backgrnd color won't change as the previous background color is set in an ID and ID trumps class notation */
#nav.sticky{
transition: 150ms;
box-shadow: 0px 15px 10px -15px #111;
background-color: #ccd6dd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="nav" :class="{sticky:active}">
<div id="nav-container" :class="toggleNavClass()">Menu
</div>
<router-view />
</div>
I just built a site using Vue.
This is my code

Add page-break function to Vue-Multipane component

I'm having problems with Vue-multipane component at my use-case and I don't find a solution.
I'm trying to disable the multipane once the screen size is smaller than 768px and display full-width rows instead of the columns. So my page is better usable on mobile.
Now I'm having two problems:
Shrinked pane doesn't get maximized to 100% if screen size is below 768px (see screenshots below). So first reduce the width of the pane with the handle and then reduce the window width below the break point 786px to see the mentioned behavior.
Handle isn't visible if reducing the screen size to 768 - 770px (close to the page-break). Page break not active but handle isn't visible. Not sure what's wrong - maybe that's easy to fix but I couldn't find a way yet.
(missing handle)
Shrinked to 767px (should be maximized to 100%) - reduce pane than reduce width of window:
It should look like this (on page load it is displayed correctly):
Please have a look at this fiddle or the code below.
But it's better to go to jsfiddle because it's easier to resize and test the mentioned behavior.
What I've tried to solve the issues:
Add css styles flex-grow: 1 and width: 100%; into the media query of the left-pane but that's not working.
For the handle issue: I've changed the break point position a bit but the behaviour was still there.
Note: I've tested the code with Firefox.
//console.log(Multipane, window)
const PageBreak = {
data() {
return {
screenWidth: document.documentElement.clientHeight,
}
},
computed: {
largeScreen () {
return this.screenWidth > 768; // 10px for handle
}
},
// bind event handlers to the `handleResize` method (defined below)
mounted: function () {
window.addEventListener('resize', this.handleResize)
},
beforeDestroy: function () {
window.removeEventListener('resize', this.handleResize)
},
methods: {
// whenever the document is resized, re-set the 'fullHeight' variable
handleResize (event) {
this.screenWidth = document.documentElement.clientWidth
}
}
}
new Vue({
el: '#app',
mixins: [ PageBreak ]
})
.custom-resizer {
width: 100%;
height: 400px;
}
.custom-resizer > .pane {
text-align: left;
padding: 15px;
overflow: hidden;
background: #eee;
border: 1px solid #ccc;
}
.custom-resizer > .multipane-resizer {
margin: 0;
left: 0;
position: relative;
}
.custom-resizer > .multipane-resizer:before {
display: block;
content: "";
width: 3px;
height: 40px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -20px;
margin-left: -1.5px;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
}
.custom-resizer > .multipane-resizer:hover:before {
border-color: #999;
}
.left-pane {
width: 50%;
}
#media (max-width: 768px) {
/* stack panes if smaller than 768px*/
.multipane.layout-v {
/*flex-direction: column;*/
display: block;
}
.left-pane {
/*flex-grow: 1;*/
/* how to maximize left-pane if it was shrinked before? */
width: 100%;
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.5.3/css/bulma.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<script src="https://unpkg.com/vue-multipane#0.9.5/dist/vue-multipane.min.js"></script>
<div id="app">
<multipane class="custom-resizer" layout="vertical">
<div class="pane left-pane">
<div>
<h6 class="title is-6">Pane 1</h6>
</div>
</div>
<multipane-resizer v-if="largeScreen"></multipane-resizer>
<div class="pane" :style="{ flexGrow: 1 }">
<div>
<h6 class="title is-6">Pane 2</h6>
</div>
</div>
<!--<multipane-resizer></multipane-resizer>
<div class="pane" :style="{ flexGrow: 1 }">
<div>
<h6 class="title is-6">Pane 3</h6>
</div>
</div>-->
</multipane>
</div>
I think I've found a way to solve the issues. (The handle issue fix with the page break position is a bit hacky but works.)
Let me explain what I did:
I added class full_width to the left pane if the screen is smaller than 768px to maximize the pane to width: 100% !important with this Vue code :class="{full_width: !largeScreen}"
The handle was missing if I'm setting the page break position to 768px. I've reduced the position by 17px (looks like the padding 15px + 2px from border - but I couldn't change this with css) then it's working. So inside the computed property of largeScreen I'm returning it like this: return this.screenWidth > (768 - 17); (I've tested this by console logging the screen size.)
Please have a look at this fiddle.
I've you're having an explanation to the handle issue and why it's working if I'm changing the position by 17px - please let me know.
The solution is OK and seems to work for me. Maybe I'm filing an issue at Vue-multipane repo. for a feature request because it's probably easier if it's directly added in the Multipane component and it would be nice if passing the break-point with a property would work.

Add Trailing Ellipsis To Breadcrumb Depending On Device Width

On my user interface I have a breadcrumb of which shows on the top bar. Upon the device width being below a defined width, it'll drop below the top bar and be it's own bar, however what I do not know how to do is add a trailing ellipsis upon the breadcrumb length being larger than the device width.
Example Breadcrumb:
<nav>
<ul>
<li>Home</li>
<li>>></li>
<li>User</li>
<li>>></li>
<li>Inbox</li>
<li>>></li>
<li>Mail_ID</li>
</ul>
</nav>
Note: >> represents a FontAwesome icon in an i tag
Upon the breadcrumb being larger than the device width, the best I can describe what I would like to happen is demonstrated below:
Home >> User >> Inbox >> Mail_ID
... User >> Inbox >> Mail_ID
... Inbox >> Mail_ID
... Mail_ID
This is still a partial code but might help you.
Idea
On load, call a function that checks for with of ul and its parent container.
If ul has greater width, hide first 2 visible li. Also add an li for ellipsis and make it hidden initially and make it visible only if any of other divs are hidden.
Repeat this process recursively and you will get what you are looking for.
Sample
$(function() {
$(".content").resizable();
$(".content").on("resize", function() {
var ul = $(this).find('ul');
if (ul.width() > $(this).width()) {
var lis = ul.find('li:not(.hide):not(.ellipsis)');
for (var i = 0; i < 2; i++) {
$(lis[i]).addClass("hide");
}
if ($(".ellipsis").not(":visible"))
$(".ellipsis").removeClass("hide")
}
})
});
.content {
text-align: left;
border: 1px solid gray;
width: 300px;
height: 40px;
max-height: 40px;
}
.content ul {
padding: 0px;
position: fixed;
overflow: hidden;
white-space: nowrap;
}
.content ul li {
display: inline-block;
text-decoration: none;
}
.hide {
display: none!important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet" />
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="content">
<ul>
<li class="hide ellipsis">...</li>
<li>Home</li>
<li>>></li>
<li>User</li>
<li>>></li>
<li>Inbox</li>
<li>>></li>
<li>Mail_ID</li>
</ul>
</div>
You can try to use the CSS-only ellipsis, but I don't know if it also works with <ul><li>. For sure it works with simple strings:
Use this HTML:
<ul class="ellipsis">
And this CSS:
ul.ellipsis
{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

"Vertical accordian" design with HTML5/CSS3?

I have an idea for my personal website layout. I'd like stacked menu items on the left side (with like 10% width) and content on the right side. By 'vertical abacus' (the original calculator with beads on a rod), I'd like menu items to appear as boxes of varying colors with a set height for each box. Because they're a set height, there will be a large portion of empty space (colored depending on what menu you select).
Utilizing the new HTML5/CSS3, I'd like to know how I'd go about creating the menu so that when you select an item, that particular item (and the items above it) slide up and stack to the top, while changing the color of the empty space below it according to the color of the respective menu item. When a menu item that is stacked at the top is selected, the items stacked below it will move back down to their original position.
First visit to the website:
After clicking 'Page2':
(I'm such an excellent MSPaint artist, I know.)
Did I lose anyone yet? :)
Would I have to tweak this process with Javascript?
I'm not asking someone to code it for me (though obviously welcome), I just have no idea where to start since W3Schools.com is frowned upon and I have an amateur knowledge of the new features in HTML5/CSS3. Would something as seemingly simple as this be difficult to begin with?
Any help is greatly appreciated! :)
Create a Fiddle for you:
http://jsfiddle.net/M8bQH/
Please adapt Width/Height and colors to your needs!
HTML:
<div id="container">
<div id="sideBar">
<ul id="myMenu">
<li class="topic1 activeItem">Home</li>
<li class="topic2">Page 2</li>
<li class="topic3">Page 3</li>
</ul>
</div>
<div class="mainContent activeContent">
Content1
</div>
<div class="mainContent">
Content2
</div>
<div class="mainContent">
Content3
</div>
</div>
JavaScript (jQuery needed!)
$('#myMenu li').click(function(){
// Set active menu item
$('#myMenu li').removeClass('activeItem');
$(this).addClass('activeItem');
// Set active content according to item
$('.mainContent').removeClass('activeContent');
$('.mainContent').eq($(this).index()).addClass('activeContent');
// Adapt background color of content according to item
$('.mainContent.activeContent').css('background-color', $(this).css('background-color'));
});
CSS:
#container {
width: 800px;
height: 600px;
}
#myMenu {
list-style-type:none;
padding: 0;
margin: 0;
}
#myMenu li {
width: 100px;
height:48px;
border-bottom: 5px solid black;
-webkit-transition: height linear 0.5s; /* For Safari 3.1 to 6.0 */
transition: height linear 0.5s;
}
#myMenu li:last-child {
border-bottom: 0px;
}
#sideBar {
width: 100px;
height: 600px;
float:left;
border-right: 5px solid black;
}
.mainContent {
width: 700px;
height: 100%;
background-color: gray;
display: none;
}
.topic1 {
background-color: gray;
}
.topic2 {
background-color: #33CCFF;
}
.topic3 {
background-color: #99FF00;
}
.activeItem {
height: 494px !important;
}
.activeContent {
display: block !important;
}

Categories

Resources