How to remove a jiggling on mouseover unwanted effect - javascript
I have the following menu—please find below the code snippet—and wanted to know if there is a possibility to remove the unwanted jiggling effect on mouse over the links (left side area). By jiggling, I don't mean the translation to the right effect. That is a well defined thing. Just try to move slowly the mouse at the very beginning of the link(s) and u'll see the unwanted effect. Any thoughts?
/* jQuery Storage API Plugin 1.7.4 https://github.com/julien-maurel/jQuery-Storage-API */
!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(e){function t(t){var r,i,n,o=arguments.length,s=window[t],a=arguments,u=a[1];if(2>o)throw Error("Minimum 2 arguments must be given");if(e.isArray(u)){i={};for(var f in u){r=u[f];try{i[r]=JSON.parse(s.getItem(r))}catch(c){i[r]=s.getItem(r)}}return i}if(2!=o){try{i=JSON.parse(s.getItem(u))}catch(c){throw new ReferenceError(u+" is not defined in this storage")}for(var f=2;o-1>f;f++)if(i=i[a[f]],void 0===i)throw new ReferenceError([].slice.call(a,1,f+1).join(".")+" is not defined in this storage");if(e.isArray(a[f])){n=i,i={};for(var m in a[f])i[a[f][m]]=n[a[f][m]];return i}return i[a[f]]}try{return JSON.parse(s.getItem(u))}catch(c){return s.getItem(u)}}function r(t){var r,i,n=arguments.length,o=window[t],s=arguments,a=s[1],u=s[2],f={};if(2>n||!e.isPlainObject(a)&&3>n)throw Error("Minimum 3 arguments must be given or second parameter must be an object");if(e.isPlainObject(a)){for(var c in a)r=a[c],e.isPlainObject(r)?o.setItem(c,JSON.stringify(r)):o.setItem(c,r);return a}if(3==n)return"object"==typeof u?o.setItem(a,JSON.stringify(u)):o.setItem(a,u),u;try{i=o.getItem(a),null!=i&&(f=JSON.parse(i))}catch(m){}i=f;for(var c=2;n-2>c;c++)r=s[c],i[r]&&e.isPlainObject(i[r])||(i[r]={}),i=i[r];return i[s[c]]=s[c+1],o.setItem(a,JSON.stringify(f)),f}function i(t){var r,i,n=arguments.length,o=window[t],s=arguments,a=s[1];if(2>n)throw Error("Minimum 2 arguments must be given");if(e.isArray(a)){for(var u in a)o.removeItem(a[u]);return!0}if(2==n)return o.removeItem(a),!0;try{r=i=JSON.parse(o.getItem(a))}catch(f){throw new ReferenceError(a+" is not defined in this storage")}for(var u=2;n-1>u;u++)if(i=i[s[u]],void 0===i)throw new ReferenceError([].slice.call(s,1,u).join(".")+" is not defined in this storage");if(e.isArray(s[u]))for(var c in s[u])delete i[s[u][c]];else delete i[s[u]];return o.setItem(a,JSON.stringify(r)),!0}function n(t,r){var n=a(t);for(var o in n)i(t,n[o]);if(r)for(var o in e.namespaceStorages)u(o)}function o(r){var i=arguments.length,n=arguments,s=(window[r],n[1]);if(1==i)return 0==a(r).length;if(e.isArray(s)){for(var u=0;u<s.length;u++)if(!o(r,s[u]))return!1;return!0}try{var f=t.apply(this,arguments);e.isArray(n[i-1])||(f={totest:f});for(var u in f)if(!(e.isPlainObject(f[u])&&e.isEmptyObject(f[u])||e.isArray(f[u])&&!f[u].length)&&f[u])return!1;return!0}catch(c){return!0}}function s(r){var i=arguments.length,n=arguments,o=(window[r],n[1]);if(2>i)throw Error("Minimum 2 arguments must be given");if(e.isArray(o)){for(var a=0;a<o.length;a++)if(!s(r,o[a]))return!1;return!0}try{var u=t.apply(this,arguments);e.isArray(n[i-1])||(u={totest:u});for(var a in u)if(void 0===u[a]||null===u[a])return!1;return!0}catch(f){return!1}}function a(r){var i=arguments.length,n=window[r],o=arguments,s=(o[1],[]),a={};if(a=i>1?t.apply(this,o):n,a._cookie)for(var u in e.cookie())""!=u&&s.push(u.replace(a._prefix,""));else for(var f in a)s.push(f);return s}function u(t){if(!t||"string"!=typeof t)throw Error("First parameter must be a string");g?(window.localStorage.getItem(t)||window.localStorage.setItem(t,"{}"),window.sessionStorage.getItem(t)||window.sessionStorage.setItem(t,"{}")):(window.localCookieStorage.getItem(t)||window.localCookieStorage.setItem(t,"{}"),window.sessionCookieStorage.getItem(t)||window.sessionCookieStorage.setItem(t,"{}"));var r={localStorage:e.extend({},e.localStorage,{_ns:t}),sessionStorage:e.extend({},e.sessionStorage,{_ns:t})};return e.cookie&&(window.cookieStorage.getItem(t)||window.cookieStorage.setItem(t,"{}"),r.cookieStorage=e.extend({},e.cookieStorage,{_ns:t})),e.namespaceStorages[t]=r,r}function f(e){var t="jsapi";try{return window[e]?(window[e].setItem(t,t),window[e].removeItem(t),!0):!1}catch(r){return!1}}var c="ls_",m="ss_",g=f("localStorage"),l={_type:"",_ns:"",_callMethod:function(e,t){var r=[this._type],t=Array.prototype.slice.call(t),i=t[0];return this._ns&&r.push(this._ns),"string"==typeof i&&-1!==i.indexOf(".")&&(t.shift(),[].unshift.apply(t,i.split("."))),[].push.apply(r,t),e.apply(this,r)},get:function(){return this._callMethod(t,arguments)},set:function(){var t=arguments.length,i=arguments,n=i[0];if(1>t||!e.isPlainObject(n)&&2>t)throw Error("Minimum 2 arguments must be given or first parameter must be an object");if(e.isPlainObject(n)&&this._ns){for(var o in n)r(this._type,this._ns,o,n[o]);return n}var s=this._callMethod(r,i);return this._ns?s[n.split(".")[0]]:s},remove:function(){if(arguments.length<1)throw Error("Minimum 1 argument must be given");return this._callMethod(i,arguments)},removeAll:function(e){return this._ns?(r(this._type,this._ns,{}),!0):n(this._type,e)},isEmpty:function(){return this._callMethod(o,arguments)},isSet:function(){if(arguments.length<1)throw Error("Minimum 1 argument must be given");return this._callMethod(s,arguments)},keys:function(){return this._callMethod(a,arguments)}};if(e.cookie){window.name||(window.name=Math.floor(1e8*Math.random()));var h={_cookie:!0,_prefix:"",_expires:null,_path:null,_domain:null,setItem:function(t,r){e.cookie(this._prefix+t,r,{expires:this._expires,path:this._path,domain:this._domain})},getItem:function(t){return e.cookie(this._prefix+t)},removeItem:function(t){return e.removeCookie(this._prefix+t)},clear:function(){for(var t in e.cookie())""!=t&&(!this._prefix&&-1===t.indexOf(c)&&-1===t.indexOf(m)||this._prefix&&0===t.indexOf(this._prefix))&&e.removeCookie(t)},setExpires:function(e){return this._expires=e,this},setPath:function(e){return this._path=e,this},setDomain:function(e){return this._domain=e,this},setConf:function(e){return e.path&&(this._path=e.path),e.domain&&(this._domain=e.domain),e.expires&&(this._expires=e.expires),this},setDefaultConf:function(){this._path=this._domain=this._expires=null}};g||(window.localCookieStorage=e.extend({},h,{_prefix:c,_expires:3650}),window.sessionCookieStorage=e.extend({},h,{_prefix:m+window.name+"_"})),window.cookieStorage=e.extend({},h),e.cookieStorage=e.extend({},l,{_type:"cookieStorage",setExpires:function(e){return window.cookieStorage.setExpires(e),this},setPath:function(e){return window.cookieStorage.setPath(e),this},setDomain:function(e){return window.cookieStorage.setDomain(e),this},setConf:function(e){return window.cookieStorage.setConf(e),this},setDefaultConf:function(){return window.cookieStorage.setDefaultConf(),this}})}e.initNamespaceStorage=function(e){return u(e)},g?(e.localStorage=e.extend({},l,{_type:"localStorage"}),e.sessionStorage=e.extend({},l,{_type:"sessionStorage"})):(e.localStorage=e.extend({},l,{_type:"localCookieStorage"}),e.sessionStorage=e.extend({},l,{_type:"sessionCookieStorage"})),e.namespaceStorages={},e.removeAllStorages=function(t){e.localStorage.removeAll(t),e.sessionStorage.removeAll(t),e.cookieStorage&&e.cookieStorage.removeAll(t),t||(e.namespaceStorages={})}});
var storage;
jQuery(function () {
storage=jQuery.localStorage;
jQuery('nav:visible ul li').click(function (e) {
//Set the aesthetics (similar to :hover)
storage.set('link',jQuery('nav:visible ul li').index(jQuery(this)));
jQuery('nav:visible ul li')
.not(".clicked").removeClass('hovered')
.filter(this).addClass("clicked hovered")
.siblings().toggleClass("clicked hovered", false);
}).hover(function () {
jQuery(this).addClass("hovered")
}, function () {
jQuery(this).not(".clicked").removeClass("hovered")
});
var pageSize = 4,
$links = jQuery(".pagedMenu li"),
count = $links.length,
numPages = Math.ceil(count / pageSize),
curPage = 1;
showPage(curPage);
function showPage(whichPage) {
var previousLinks = (whichPage - 1) * pageSize,
nextLinks = (previousLinks + pageSize);
$links.show();
$links.slice(0, previousLinks).hide();
$links.slice(nextLinks).hide();
showPrevNext();
}
function showPrevNext() {
if ((numPages > 0) && (curPage < numPages)) {
jQuery("#nextPage").removeClass('hidden');
jQuery("#msg").text("(" + curPage + " of " + numPages + ")");
} else {
jQuery("#nextPage").addClass('hidden');
}
if ((numPages > 0) && (curPage > 1)) {
jQuery("#prevPage").removeClass('hidden');
jQuery("#msg").text("(" + curPage + " of " + numPages + ")");
} else {
jQuery("#prevPage").addClass('hidden');
}
}
jQuery("#nextPage").on("click", function () {
showPage(++curPage);
storage.set('page',curPage);
});
jQuery("#prevPage").on("click", function () {
showPage(--curPage);
storage.set('page',curPage);
});
if(storage.isSet('page')){
var page= storage.get('page');
curPage = page;
showPage(page);
}
if(storage.isSet('link')){
var link = storage.get('link')+1;
jQuery('nav:visible ul li:nth-child('+link+')').addClass("clicked hovered");
var newUrl = jQuery('nav:visible ul li:nth-child('+link+') a').attr('href');
if(newUrl!=location.href)location.href=newUrl;
}
});
.hidden {
display: none;
}
body {
font: normal 1.0em Arial, sans-serif;
}
nav.pagedMenu {
color: red;
font-size: 2.0em;
line-height: 1.0em;
width: 8em;
position: fixed;
top: 50px;
}
nav.pagedMenu ul {
list-style: none;
margin: 0;
padding: 0;
}
nav.pagedMenu ul li {
height: 1.0em;
padding: 0.15em;
position: relative;
border-top-right-radius: 0em;
border-bottom-right-radius: 0em;
-webkit-transition:
-webkit-transform 220ms, background-color 200ms, color 500ms;
transition: transform 220ms, background-color 200ms, color 500ms;
}
nav.pagedMenu ul li.hovered {
-webkit-transform: translateX(1.5em);
transform: translateX(1.5em);
}
nav ul li:hover a {
transition: color, 1200ms;
color: red;
}
nav.pagedMenu ul li span {
display:block;
font-family: Arial;
position: absolute;
font-size:1em;
line-height: 1.25em;
height:1.0em;
top:0; bottom:0;
margin:auto;
right: 0.01em;
color: #F8F6FF;
}
a {
color: gold;
transition: color, 1200ms;
text-decoration: none;
}
#pagination, #prevPage, #nextPage {
font-size: 1.0em;
color: gold;
line-height: 1.0em;
padding-top: 250px;
padding-left: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<nav class="pagedMenu">
<ul style="font-size: 28px;">
<li class="" style="margin-bottom: 5px;">Link 1</li>
<li class="" style="margin-bottom: 5px;">Link 2</li>
<li class="" style="margin-bottom: 5px;">Link 3</li>
<li class="" style="margin-bottom: 5px;">Link 4</li>
<li class="" style="margin-bottom: 5px;">Link 5</li>
<li class="" style="margin-bottom: 5px;">Link 6</li>
<li class="" style="margin-bottom: 5px;">Link 7</li>
<li class="" style="margin-bottom: 5px;">Link 8</li>
<li class="" style="margin-bottom: 5px;">Link 9</li>
<li class="" style="margin-bottom: 5px;">Link 10</li>
<li class="" style="margin-bottom: 5px;">Link 11</li>
<li class="" style="margin-bottom: 5px;">Link 12</li>
</ul>
</nav>
<div id="pagination">
Previous
Next
<span id="msg"></span>
</div>
Jsfiddle here
I think you don't need jQuery at all
Replace:
nav.pagedMenu ul li.hovered {
-webkit-transform: translateX(1.5em);
transform: translateX(1.5em);
}
nav ul li:hover a {
transition: color, 1200ms;
color: red;
}
With:
nav.pagedMenu ul li:hover a {
transition: padding-left 500ms,color 1200ms;
padding-left:1.5em;
color: red;
}
http://jsfiddle.net/dh7920rq/
The problem with your implementation is that: when your <li> is hovered, it moves to the right, but your mouse is still at the original position, causing mouseout immediately which causes the unwanted effect.
If we don't move the <li>, but instead increase the padding-left of the <a>, the mouse is still inside the <li> (or increase margin-left of the <a>)
http://jsfiddle.net/dh7920rq/1/
Updated:
Use jQuery to keep the state as requested by the OP:
Replace:
nav.pagedMenu ul li.hovered {
-webkit-transform: translateX(1.5em);
transform: translateX(1.5em);
}
nav ul li:hover a {
transition: color, 1200ms;
color: red;
}
With:
nav.pagedMenu ul li.hovered a{
transition: margin-left 500ms,transition: color 1200ms;
margin-left:1.5em;
color: red;
}
The idea is the same: http://plnkr.co/edit/880ZtUiCwbRNGBNGuURU?p=preview
You can do this effect by giving padding to the a element.
Jsfiddle
Related
jQuery Navigation Carousel
I'm trying to make a carousel-like navigation and subnavigation. This script works almost as I expected: https://jsfiddle.net/xpvt214o/23270/ Here is how it should work: If the navigation overflows, make next arrow visible. If we scroll right, make the left arrow visible. Hide both arrows if the navigation does not overflow. In the first script I have a few problems: 1) Prev and next arrows from both navs work for first nav 2) When I scroll right and really get to the end of the navigation, I need to click the next arrow once more before it hides. To make appropriate arrows work with their navigations I wrap my main code in a function and replace $('.go-left') to nav.find('.go-left') and same for the right arrow. Here's the code: https://jsfiddle.net/0z5u4vyt/5/ The arrows are supposed to work but they don't. Please, I need your help!
in your javascript code var prevArrow = nav.find('.go-left'); and var nextArrow = nav.find('.go-right'); don't get value so change your code to var prevArrow = nav.siblings('.go-left'); var nextArrow = nav.siblings('.go-right'); function navCarousel(el) { var nav = el; var navFirstItem = nav.find('li:first-child'); var navLastItem = nav.find('li:last-child'); var prevArrow = nav.siblings('.go-left'); var nextArrow = nav.siblings('.go-right'); function checkNavOverflow() { if (nav.prop('scrollWidth') > nav.width() && (nav.find('ul').width() - navLastItem.offset().left) < 51) { nextArrow.css('display', 'block'); } else { nextArrow.css('display', 'none'); } if (navFirstItem.offset().left < 15) { prevArrow.css('display', 'block'); } else { prevArrow.css('display', 'none'); } } checkNavOverflow(); $(window).on('resize', function() { checkNavOverflow(); // console.log(nav.find('ul').width() - navLastItem.offset().left); }); prevArrow.click(function() { var pos = nav.scrollLeft() - 200; nav.animate( { scrollLeft: pos }, 300, checkNavOverflow()); }); nextArrow.click(function() { var pos = nav.scrollLeft() + 200; nav.animate( { scrollLeft: pos }, 300, checkNavOverflow()); }); } navCarousel($('.category-navigation .category-navigation-container')); navCarousel($('.subcategory-navigation .subcategory-navigation-container')); body { background: #20262e; padding: 0; margin: 0; font-family: Helvetica; } .category-navigation, .subcategory-navigation { background-color: #fff; position: relative; border-bottom: 1px solid grey; } .category-navigation-container, .subcategory-navigation-container { overflow: hidden; } .category-navigation ul, .subcategory-navigation ul { list-style: none; display: flex; white-space: nowrap; } .category-navigation ul li, .subcategory-navigation ul li { padding: 20px; } .category-navigation .go-left, .subcategory-navigation .go-left, .category-navigation .go-right, .subcategory-navigation .go-right { display: none; position: absolute; top: 50%; transform: translateY(-50%); cursor: pointer; width: 20px; height: 20px; background-color: grey; z-index: 9; border-radius: 50%; padding: 10px; text-align: center; } .category-navigation .go-left, .subcategory-navigation .go-left { left: 0; } .category-navigation .go-right, .subcategory-navigation .go-right { right: 0; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="category-navigation"> <div class="go-left">←</div> <div class="category-navigation-container"> <ul> <li>Category Item 1</li> <li>Category Item 2</li> <li>Category Item 3</li> <li>Category Item 4</li> <li>Category Item 5</li> <li>Category Item 6</li> <li>Category Item 7</li> <li>Category Item 8</li> <li>Category Item 9</li> </ul> </div> <div class="go-right">→</div> </div> <div class="subcategory-navigation dropdown"> <div class="go-left">←</div> <div class="subcategory-navigation-container"> <ul> <li> <a href="#"> <div>Subitem 1</div> </a> </li> </ul> </div> <div class="go-right">→</div> </div>
How to get updated top position of scrolled up div?
http://codepen.io/leongaban/pen/YNBgqE After you scroll the #tickers-col how do you get the correct updated top position of the div? From the log no matter how far up the column goes, it still displays the original y position of the div: colPos Object {x: 8, y: 8} const tickersCol = document.getElementById("tickers-col"); // https://stackoverflow.com/questions/288699/get-the-position-of-a-div-span-tag function getPos(el) { for (var lx=0, ly=0; el != null; lx += el.offsetLeft, ly += el.offsetTop, el = el.offsetParent); return {x: lx,y: ly}; } function mouseHover() { const colPos = getPos(tickersCol); console.log('colPos', colPos) } .container { position: fixed; font-family: Arial; } #tickers-col { overflow-y: auto; height: 400px; } li { margin-bottom: 10px; list-style: none; padding: 5px 10px; width: 120px; color: white; background: salmon; border-radius: 4px; cursor: pointer; } <div class="container"> <div id="tickers-col"> <ul> <li onmouseenter="mouseHover()">aaa</li> <li onmouseenter="mouseHover()">bbb</li> <li onmouseenter="mouseHover()">ccc</li> <li onmouseenter="mouseHover()">ddd</li> <li onmouseenter="mouseHover()">eee</li> <li onmouseenter="mouseHover()">fff</li> <li onmouseenter="mouseHover()">ggg</li> <li onmouseenter="mouseHover()">hhh</li> <li onmouseenter="mouseHover()">iii</li> <li onmouseenter="mouseHover()">jjj</li> <li onmouseenter="mouseHover()">kkk</li> <li onmouseenter="mouseHover()">lll</li> <li onmouseenter="mouseHover()">mmm</li> <li onmouseenter="mouseHover()">nnn</li> <li onmouseenter="mouseHover()">ooo</li> <li onmouseenter="mouseHover()">ppp</li> <li onmouseenter="mouseHover()">qqq</li> <li onmouseenter="mouseHover()">rrr</li> <li onmouseenter="mouseHover()">sss</li> <li onmouseenter="mouseHover()">ttt</li> <li onmouseenter="mouseHover()">uuu</li> <li onmouseenter="mouseHover()">vvv</li> <li onmouseenter="mouseHover()">www</li> <li onmouseenter="mouseHover()">yyy</li> <li onmouseenter="mouseHover()">xxx</li> <li onmouseenter="mouseHover()">zzz</li> </ul> </div> </div> Found the getPos function from here: Get the position of a div/span tag
So, I really don't understand what exactly you are trying to do but by definition the offsetTop property of an element will return the top position (in pixels) relative to the top of the offsetParent element. So no matter how long you scroll, if the top distance between the element you are inspecting and it's parent does't change, your top value will not change. Maybe the property you are looking for is scrollTop; const getPos = (el) => { for (var lx=0, ly=0; el != null; lx += el.offsetLeft, ly += el.scrollTop, el = el.offsetParent); return { x:lx, y:ly }; }
Try the code blow. Scroll and then hover on one of the divs, you can see the y-coordinate is getting updated. const tickersCol = document.getElementById("tickers-col"); const getPos = (el) => { for (var lx=0, ly=0; el != null; lx += el.offsetLeft, ly += el.scrollTop, el = el.offsetParent); return { x:lx, y:ly }; } function mouseHover() { const colPos = getPos(tickersCol); console.log('colPos', colPos) } .container { position: fixed; font-family: Arial; } #tickers-col { overflow-y: auto; height: 400px; } li { margin-bottom: 10px; list-style: none; padding: 5px 10px; width: 120px; color: white; background: salmon; border-radius: 4px; cursor: pointer; } <div class="container"> <div id="tickers-col"> <ul> <li onmouseenter="mouseHover()">aaa</li> <li onmouseenter="mouseHover()">bbb</li> <li onmouseenter="mouseHover()">ccc</li> <li onmouseenter="mouseHover()">ddd</li> <li onmouseenter="mouseHover()">eee</li> <li onmouseenter="mouseHover()">fff</li> <li onmouseenter="mouseHover()">ggg</li> <li onmouseenter="mouseHover()">hhh</li> <li onmouseenter="mouseHover()">iii</li> <li onmouseenter="mouseHover()">jjj</li> <li onmouseenter="mouseHover()">kkk</li> <li onmouseenter="mouseHover()">lll</li> <li onmouseenter="mouseHover()">mmm</li> <li onmouseenter="mouseHover()">nnn</li> </ul> </div> </div> Let me know if this is what you wanted.
After you scroll the #tickers-col how do you get the correct updated top position of the div? div itself isn't moving when scrolling, instead only its direct children are moving. So top position is : #ticker-col's offsetTop - ul's scrollTop , and your js code should look like this: (function(){ var tc = document.querySelector("#tickers-col"); tc.addEventListener("scroll",function(){ console.log("colPos:", { x : tc.parentNode.offsetLeft, y : tc.parentNode.offsetTop - tc.scrollTop, }); }); })(); .container { position: fixed; font-family: Arial; } #tickers-col { overflow-y: auto; height: 400px; } li { margin-bottom: 10px; list-style: none; padding: 5px 10px; width: 120px; color: white; background: salmon; border-radius: 4px; cursor: pointer; } <div class="container"> <div id="tickers-col"> <ul> <li>aaa</li> <li>bbb</li> <li>ccc</li> <li>ddd</li> <li>eee</li> <li>fff</li> <li>ggg</li> <li>hhh</li> <li>iii</li> <li>jjj</li> <li>kkk</li> <li>lll</li> <li>mmm</li> <li>nnn</li> <li>ooo</li> <li>ppp</li> <li>qqq</li> <li>rrr</li> <li>sss</li> <li>ttt</li> <li>uuu</li> <li>vvv</li> <li>www</li> <li>yyy</li> <li>xxx</li> <li>zzz</li> </ul> </div> </div> P.S. onmouseenter="mouseHover()" this thing looks ugly, if you need to set the same function for some number of elements > 2 as event listener the better approach would be like so var collection = Array.prototype.slice.call( document.querySelectorAll('mycssselector') ).forEach(function(el){ el.addEventListener("myevent",/*callback*/function(){}); });
Detect data change from different DOM element
TL;DR: Detect item change from the actual <ul> list and persist the data Howdy everyone? I'm currently doing a Trello-like based web-application using PHP as a backend and jQueryUI as a front-end. The front-end part is made using sortable(), by defining three UL elements. One is a container / wrapper with the id Nav and the other two are actual boards that hold the items. Case scenarios are simple: You can reorder boards You can move order of items inside the single board You can move item from one board to another The included code supports all three of them but the data should persist to the back-end powered database (I'm currently on SQLite since the project is in early phase). Problem The method setSortAction currently detects all three use case but once you move the item from one board to another the order of the list can't be properly detected (since they are in incremented value). Getting the bodyContent like this: action=updateMenuItemListings&record=2&record=1&record=3 by moving the second item to the first place in the board is fine, and I can persist that change through the POST request on back-end and then onto the database. What happens when you move the first item from the second board on the first board? You'd get value of bodyContent similar to this: action=updateMenuItemListings&record=1&record=2&record=1&record=3 As you can see the record with value 1 duplicates. That means I can't detect the item moved is from the second board and I have duplicate items in the order of the board. How would you go about designing this? Can it be done by the given code or have I totally missed the logic that one should apply in this scenario? Thank you. $(function() { var debugMode = true; $("ul.droptrue").sortable({ connectWith: "ul" }); //Set common sort settings for all lists $(".sortable").sortable({ opacity: 0.6, cursor: 'move' }); //Function used to configure update calls for each sort function setSortAction(selector, updatePage, updateAction, itemLabel) { $(selector).sortable({ update: function() { var itemList = $(this).sortable( "serialize", { attribute: "id", key: itemLabel }); //Create POST request to persist the update var bodyContent = "action=" + updateAction + "&" + itemList; if (debugMode) { alert("DEBUG: bodyContent = \n" + bodyContent); } //$.post(updatePage, bodyContent, function (postResult) //{ alert(postResult); }); } }); } //Set sort update action for top level and second level setSortAction(".navLevel1", "reorder.php", "updateMenuListings", "record"); setSortAction(".navLevel2", "reorder.php", "updateMenuItemListings", "record"); }); #import url( 'https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css' ); #sortable_1, #sortable_2, #sortable_3 { list-style-type: none; margin: 0; float: left; margin-right: 10px; background: #eee; padding: 5px; width: 143px; } #sortable_1 li, #sortable_2 li, #sortable_3 li { margin: 5px; padding: 5px; font-size: 1.2em; width: 120px; } body { font-family: Arial, Helvetica, sans-serif; } table { font-size: 1em; } .ui-draggable, .ui-droppable { background-position: top; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <ul id="Nav" class="sortable navLevel1"> <ul id="sortable_1" class="droptrue navLevel2"> <li class="ui-state-disabled" style="opacity: 1; pointers-event: none; background: yellow">Classes</li> <li id="item_1" class="ui-state-default">Some class</li> <li id="item_2" class="ui-state-default">Another one!</li> <li id="item_3" class="ui-state-default">Yep, thats enough</li> </ul> <ul id="sortable_2" class="droptrue navLevel2"> <li class="ui-state-disabled" style="opacity: 1; pointers-event: none; background: yellow">Presentation</li> <li id="item_1" class="ui-state-default">Tom</li> <li id="item_2" class="ui-state-default">Jessica</li> <li id="item_3" class="ui-state-default">Kevin</li> </ul> </ul> <br style="clear:both">
Unlike classes HTML ID's should be unique, in this way you can identify which items are from which columns. Knowing for example that column one has 4 slots and column two has 6 would mean that a request array of 7,3,9,3,2,5,6,1,4,8,10 gets split into 4 and 6 hence Column one: 7, 3, 9, 10, Column two: 2, 5, 6, 1, 4, 8 $(function() { var debugMode = true; $("ul.droptrue").sortable({ connectWith: "ul" }); //Set common sort settings for all lists $(".sortable").sortable({ opacity: 0.6, cursor: 'move' }); //Function used to configure update calls for each sort function setSortAction(selector, updatePage, updateAction, itemLabel) { $(selector).sortable({ update: function() { var itemList = $(this).sortable( "serialize", { attribute: "id", key: itemLabel }); //Create POST request to persist the update var bodyContent = "action=" + updateAction + "&" + itemList; if (debugMode) { $('#report').text("DEBUG: bodyContent = \n" + bodyContent); } //$.post(updatePage, bodyContent, function (postResult) //{ alert(postResult); }); } }); } //Set sort update action for top level and second level setSortAction(".navLevel1", "reorder.php", "updateMenuListings", "record"); setSortAction(".navLevel2", "reorder.php", "updateMenuItemListings", "record"); }); #import url( 'https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css' ); #sortable_1, #sortable_2, #sortable_3 { list-style-type: none; margin: 0; float: left; margin-right: 10px; background: #eee; padding: 5px; width: 143px; } #sortable_1 li, #sortable_2 li, #sortable_3 li { margin: 5px; padding: 5px; font-size: 1.2em; width: 120px; } body { font-family: Arial, Helvetica, sans-serif; } table { font-size: 1em; } .ui-draggable, .ui-droppable { background-position: top; } #report { position: fixed; font-size: 0.5em; bottom: 2em; left: 1em; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <ul id="Nav" class="sortable navLevel1"> <ul id="sortable_1" class="droptrue navLevel2"> <li class="ui-state-disabled" style="opacity: 1; pointers-event: none; background: yellow">Classes</li> <li id="item_1" class="ui-state-default">Some class</li> <li id="item_2" class="ui-state-default">Another one!</li> <li id="item_3" class="ui-state-default">Yep, thats enough</li> </ul> <ul id="sortable_2" class="droptrue navLevel2"> <li class="ui-state-disabled" style="opacity: 1; pointers-event: none; background: yellow">Presentation</li> <li id="item_4" class="ui-state-default">Tom</li> <li id="item_5" class="ui-state-default">Jessica</li> <li id="item_6" class="ui-state-default">Kevin</li> </ul> </ul> <div id="report"></div> <br style="clear:both">
Accessible jQuery toggles odd behaviour
I'm setting up a reusable jQuery toggle function (for my employer) to enable show/hide of content (eg FAQs), to improve accessibility over existing inline onClick bindings. In JSFiddle everything is fast and fluid, but when I transfer to the preview server an odd behaviour becomes apparent. Clicking the button works as normal, but on clicking the button's link text there's a noticeable delay before anything happens. Is there anything within the script that might be causing the delay? Or anything which is an obvious candidate for conflicts of some kind? Anyway, here's the code (based on Mike Raynham's accessible toggles): http://jsfiddle.net/internet_man/9yKKM/2/ html: <div class="buttons"> <ul> <li><strong class="_toggle type noscript">This is the first toggle</strong> <ul class="_toggle-this show-hide"> <li><strong>Option One</strong></li> <li><strong>Option Two</strong></li> <li><strong>Option Three</strong></li> </ul> </li> <li class="bad-break"><strong class="_toggle type noscript">This is the second toggle (a bit longer than the first)</strong> <ul class="_toggle-this show-hide"> <li><strong>Option One</strong></li> <li><strong>Option Two</strong></li> </ul> </li> <li class="bad-break"><strong class="_toggle type noscript">This is the third toggle (also somewhat longer)</strong> <ul class="_toggle-this show-hide"> <li><strong>Option One</strong></li> <li><strong>Option Two</strong></li> </ul> </li> </ul> </div> Script: var create_name = function(text) { var name = text.toLowerCase(); name = name.replace(/^\s+|\s+jQuery|[^a-z0-9&\s-]/g, ''); name = name.replace(/&/g, 'and'); name = name.replace(/\s/g, '-'); name = name.replace(/(-)+\1/g, "jQuery1"); return name; }; var add_link = function() { var name = create_name(jQuery(this).text()); jQuery(this).next('.toggle-this').attr('name', name); jQuery(this).html('' + jQuery(this).html() + ''); }; var toggle = function(event) { event.preventDefault(); jQuery(this).toggleClass('expanded').nextAll('.toggle-this').slideToggle(100); jQuery('.toggle-this').not(jQuery(this).siblings()).slideUp(100); jQuery('.toggle').not(jQuery(this)).removeClass('expanded'); }; var remove_focus = function() { jQuery(this).blur(); }; jQuery(function (){ jQuery('._toggle').removeClass('_toggle, noscript').addClass('toggle'); jQuery('._toggle-this').removeClass('_toggle-this').addClass('toggle-this'); jQuery('._expanded').removeClass('_expanded').addClass('expanded'); jQuery('.toggle:not(.expanded)').nextAll('.toggle-this').hide(); jQuery('.toggle').each(add_link); jQuery('.toggle').click(toggle); jQuery('.toggle a').mouseup(remove_focus); }); CSS: body { font-size:62.5%; color:#666; font-family:arial,Helvetica,sans-serif; } a { color:#462170; } ul { list-style-type:none; margin-left:0; padding-left:0; } strong { font-weight:normal; } div.buttons { width:462px; line-height:2.2em; margin:1.5em 0; } .buttons li > strong { font-size:1.9em; } .toggle, .buttons .type.noscript { display:block; font-size:1.9em; height:65px; background:url(http://oi48.tinypic.com/dy6xf.jpg) 0 -85px no-repeat; padding:20px 0 0 90px; text-decoration:none; color:#462170; cursor:pointer; cursor:hand; } .toggle a { text-decoration:none; } .toggle strong { display:block; height:65px; border:1px dotted red; } .toggle:hover, .toggle:focus { background-position:0 0; } .buttons .show-hide { border-bottom:6px solid white; } .buttons .show-hide li { margin-left:12px; border-bottom: 2px solid white; border-top:2px solid white; font-size:1em; } .buttons .show-hide a { display:block; height:15px; text-decoration:none; color:#462170; background:#f1f1f1 url(http://oi46.tinypic.com/6iedtw.jpg) 5px 14px no-repeat; padding:12px 0 15px 58px; } .buttons .show-hide a:hover, .connection-buttons .show-hide a:focus { text-decoration:underline; color:black; background-color:#ece8f0; background-position:5px -41px; } li.bad-break a { padding-right:30%; } Notes: Scripts on -- ._toggle becomes .toggle, ._toggle-this becomes .toggle-this, .noscript is removed and a link is inserted around (in this case) the strong element of class toggle, pointing to the next '.toggle-this' element. Scripts off -- No toggle created but it's styled as though the accordions are expanded.
I want to show list items as 2 or more columns (dynamic alignment)
I am able to do the list using float:left; like this But I would like to show it like this (as 2 or more columns) How can I do that? #sandeep gave good solution. Unfortunately does not work in IE(need ie7 and above). any help?
For this, you can use the column-count property: div#multicolumn1 { -moz-column-count: 2; -moz-column-gap: 50%; -webkit-column-count: 2; -webkit-column-gap: 50%; column-count: 3; column-gap: 50%; } Check this jsFiddle. Note: It does not work in IE. For IE, you can use this JavaScript: CSS3 - Multi Column Layout Demonstration
This works just fine cross-browser, no JS required. You just limit the width of your columns. <style> ul.col {width:60px;float:left;margin:0 5px 0 0;padding:0;} ul.col li {width:50px;background:#999;list-style-type:none;margin:0 0 5px 0;padding:5px;} </style> <ul class="col"> <li>1(li)</li> <li>2(li)</li> <li>3(li)</li> <li>4(li)</li> <li>5(li)</li> </ul> <ul class="col"> <li>6(li)</li> <li>7(li)</li> <li>8(li)</li> <li>9(li)</li> <li>10(li)</li> </ul> If you are stuck with them all in one UL on page load, you can split them out with jQuery to create the same results: <script> $(function(){ //on document ready var perCol = 5; var $ul = $('ul.col'); var rows = Math.ceil($ul.find('li').length/perCol); for(var i=1;i<=rows;i++){ $ul.after('<ul class="col"></ul>'); } for(var i=1;i<=rows;i++){ $ul.find('li:lt('+(perCol)+')').appendTo('ul.col:eq('+(i)+')'); } $ul.remove(); }); </script>
As long as your list items are a fixed width, like in your examples, couldn't you just do something like in this fiddle? http://jsfiddle.net/swNYE/1/ And wherever your list gets spit out, simply apply the "left" class to the first half, and the "right" class to the second half. If you're dynamically adding content through Javascript, then you'd simply run through the li's each time something is added, and apply the new correct class. HTML: <ul> <li class="left">1</li> <li class="left">2</li> <li class="left">3</li> <li class="left">4</li> <li class="right">5</li> <li class="right">6</li> <li class="right">7</li> <li class="right">8</li> </ul> CSS: li { width: 100px; } li.left { float: left; clear: left; } li.right { margin-left: 100px; }
Below is a columnizer, takes as argument the number of columns. Call: $(elem).columnize(3) http://jsfiddle.net/Bdsj9/28/ Tested in IE6 from wine in Ubuntu 10.04: works (looks better if you increase the width in the stylesheet I borrowed from #micha -- thanks, btw) (function($) { $.fn.decolumnize = function() { this.children().map(function(index, el) { var oldPos = null; var posClass = null; if($(el).attr("class") && (posClass = $(el).attr("class").match(/orig\-readorder\-[0-9]+/))) { oldPos = parseInt(posClass[0].replace("orig-readorder-", "")); $(el).removeClass(posClass[0]); } return { elm: el, pos: oldPos ? oldPos : index } }).sort(function(a,b) { return a.pos > b.pos ? 1 : -1; }).map(function(index, ob) { return ob.elm; }).appendTo(this); return this.children().css("float", "left").css("clear", "none"); }; $.fn.columnize = function(numcols) { var numItems = this.children().length; var divisor = Math.ceil(numItems / numcols); var indexOfFinal = null; this.decolumnize(); var resorted = this.children().map(function(index, el) { return { position: (index % divisor) + (index / numItems), elm: el, isFinal: index == numItems - 1, origPos: index }; }).sort(function(a, b) { return a.position > b.position ? 1 : -1; }); return resorted.map(function(index, ob) { if (indexOfFinal) { /** TODO: fix redundancy **/ if ((index - indexOfFinal - 1) % (numcols - 1) == 0) { if ($.browser.msie && resorted[index - 1]) $(resorted[index - 1].elm).css("float", "none"); $(ob.elm).css("clear", "left"); } } else { /** TODO: fix redundancy **/ if (index % numcols == 0) { if ($.browser.msie && resorted[index - 1]) $(resorted[index - 1].elm).css("float", "none"); $(ob.elm).css("clear", "left"); } } if (ob.isFinal) indexOfFinal = index; $(ob.elm).addClass("orig-readorder-" + ob.origPos); return ob.elm; }).appendTo(this); }; })(jQuery); What it does is first calculate the sortorder, because with just styling this will not work. To calculate the sortorder you need to know the number of columns up front. This you can use as a divisor to introduce a 'clear: left'. Also, using the number of list items and the number of columns you need to estimate a number of rows. This can be used as a divisor to sort the items based on the remainer between their index and the number of rows. To granulate the sortation, the original index is also taken into account... After the last orginal item has been placed, the number of columns needs to be reduced by 1. That's why I store the isFinal-boolean. I'm sure this could be done more elegantly with some smart computation, though. Then introduce the 'clear: left's at the right place and store the original position in a class so you can resort in a later stage (for instance when inserting or removing a list item dynamically) Best!
I found a way to do this in IE too. (using clear) html: <div class="left child">1</div> <div class="child">5</div> <div class="left child">2</div> <div class="child">6</div> <div class="left child">3</div> <div class="child">7</div> <div class="left child">4</div> <div class="child">8</div> css: .child { height:20px; width: 20px; text-align: center; border: 1px solid #CCC; background-color: #EEE; color: #000; padding: 5px; float: left; margin: 5px; } .left { clear: left; } See http://jsfiddle.net/pMbtk/31/
SEE MY NEW ANSWER--MUCH BETTER THAN THIS If you are dealing with fixed width items, then this pure css solution that works in IE7+. The example is http://jsfiddle.net/VVMnq/33/. This would require you to know some things about the html you are working with (where the new column is to start). Here is column 2 longer, just to see how it handles it: http://jsfiddle.net/VVMnq/42/ HTML <ul class="TwoColumn"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li class="column2">6</li> <li class="column2">7</li> <li class="column2">8</li> <li class="column2">9</li> <li class="column2">10</li> </ul> CSS .TwoColumn { width: 20px; padding-left: 22px; /* width plus borders of li's */ } .TwoColumn li { display: block; width: 100%; padding: 0; margin: 0 -22px 0 -22px; /* width of ul plus borders on li's */ float: left; clear: left; border: 1px solid blue; } .TwoColumn .column2 { float: none; /* this could be set to float: left and it seemed to work also */ clear: none; margin: 0 -11px 0 0; /* this should be half of margins for column 1 li's */ }
The CSS: ul.parent li { float: left; } Using jquery: $('.parent>li:odd').css("clear", "both"); <ul class="parent"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> </ul> Please take a look at http://api.jquery.com/even-selector/ and http://api.jquery.com/odd-selector/
NEW ANSWER that is totally different from the first. This assumes the height is always 5 high (which from comments on jblasco's solution made by anglimas, that is the requirement). The solution here is pure css (though if the number of elements is unknown, then some javascript would be required to "count" and set a class called first to indicate which element is in the first row). The solution works in IE7+, and will accommodate any number of columns. See http://jsfiddle.net/VVMnq/107/. HTML <ul class="MultiColumn"> <li class="first">1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li class="first">6</li> <li>7</li> <li>8</li> <li>9</li> <li>10</li> </ul> CSS .MultiColumn { overflow: auto; background-color: yellow; padding:0 10px 10px 0; float: left; } .MultiColumn li { display: block; width: 20px; height: 20px; padding: 0; margin: 10px 0px 10px 10px ; background-color: cyan; float: left; } .MultiColumn li.first { top: 0px; } .MultiColumn li.first + li { margin: 40px 0 0 -20px; } .MultiColumn li.first + li + li { margin: 70px 0 0 -20px; } .MultiColumn li.first + li + li + li { margin: 100px 0 0 -20px; } .MultiColumn li.first + li + li + li + li { margin: 130px 0 0 -20px; }
http://jsfiddle.net/rlemon/Y5ZvA/3/ you can try this.. I haven't tested it with ie yet. ul { width:60px; height: 60px; } ul li{ float:left; width:20px; list-style:none; } ul, ul li { -moz-transform: rotate(-90deg) scaleX(-1); -o-transform: rotate(-90deg) scaleX(-1); -webkit-transform: rotate(-90deg) scaleX(-1); -ms-transform: rotate(-90deg) scaleX(-1); transform: rotate(-90deg) scaleX(-1); /* IE8+ - must be on one line, unfortunately */ -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=-3.061616997868383e-16, M12=1, M21=1, M22=3.061616997868383e-16, SizingMethod='auto expand')"; /* IE6 and 7 */ filter: progid:DXImageTransform.Microsoft.Matrix( M11=-3.061616997868383e-16, M12=1, M21=1, M22=3.061616997868383e-16, SizingMethod='auto expand'); }