Detect data change from different DOM element - javascript

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">

Related

Is my code to change the CSS of an element when clicked efficient?

I want to know if there is a more efficient way to change the CSS of multiple elements when one is clicked.
I have working code whereby clicking on a subheading within the "About" section changes its color, the color of the other subheadings, and the opacity of reach respective paragraph. It looks like a lot of code to me. I imagine there is a simpler or at least more streamlined way to accomplish these things that I just don't know with my borderline intermediate JS skills.
const about = () => {
const paraOne = document.getElementById("para-one");
const paraTwo = document.getElementById("para-two");
const paraThree = document.getElementById("para-three");
const mission = document.querySelector(".mission");
const value = document.querySelector(".value");
const vision = document.querySelector(".vision");
mission.addEventListener('click', () => {
mission.classList.add("active");
value.classList.remove("active");
vision.classList.remove("active");
paraOne.classList.remove('zero');
paraTwo.classList.add('zero');
paraThree.classList.add('zero');
});
value.addEventListener('click', () => {
value.classList.add("active");
mission.classList.remove("active");
vision.classList.remove("active");
paraOne.classList.add('zero');
paraTwo.classList.remove('zero');
paraThree.classList.add('zero');
});
vision.addEventListener('click', () => {
vision.classList.add("active");
mission.classList.remove("active");
value.classList.remove("active");
paraOne.classList.add('zero');
paraTwo.classList.add('zero');
paraThree.classList.remove('zero');
});
}
about();
#about-center ul li:hover,
#about-center ul li.active {
color: black;
cursor: pointer;
}
.active {
color: black;
cursor: pointer;
}
#container {
position: relative;
}
#container p {
position: absolute;
left: 30%;
}
.zero {
opacity: 0;
}
#about ul,
#about p,
#about button {
margin-bottom: 2em;
}
#about-center p {
font-size: 2rem;
}
<div id="about-center">
<h1>About Us</h1>
<ul>
<li class="mission active">OUR MISSION</li>
<li class="value">our values</li>
<li class="vision">our vision</li>
</ul>
<div id="container">
<p id="para-one">This is our mission.
</p>
<p id="para-two" class="zero">These are our values.
</p>
<p id="para-three" class="zero">This is our vision.
</p>
</div>
<button>read more</button>
</div>
You can loop over all the li elements. Use this to add the event listeners initially. Then in the listener, you can loop again to do the appropriate class changes for the element that the user clicked on versus the other elements. And you can use the index of the iteration to change the class of the corresponding p element.
const about = () => {
const lis = document.querySelectorAll("#about-list li");
const paras = document.querySelectorAll("#container p");
lis.forEach(li => li.addEventListener("click", (e) => lis.forEach((li1, index) => {
if (li1 == e.target) {
li1.classList.add("active");
paras[index].classList.remove("zero");
} else {
li1.classList.remove("active");
paras[index].classList.add("zero");
}
})));
}
about();
#about-center ul li:hover,
#about-center ul li.active {
color: black;
cursor: pointer;
}
.active {
color: black;
cursor: pointer;
}
#about-center ul li {
color: grey;
}
#container {
position: relative;
}
#container p {
position: absolute;
left: 30%;
}
.zero {
opacity: 0;
}
#about ul,
#about p,
#about button {
margin-bottom: 2em;
}
#about-center p {
font-size: 2rem;
}
<div id="about-center">
<h1>About Us</h1>
<ul id="about-list">
<li class="mission active">OUR MISSION</li>
<li class="value">our values</li>
<li class="vision">our vision</li>
</ul>
<div id="container">
<p id="para-one">This is our mission.
</p>
<p id="para-two" class="zero">These are our values.
</p>
<p id="para-three" class="zero">This is our vision.
</p>
</div>
<button>read more</button>
</div>
I think this is as effective as it could be. Now, there are things which can be done to make it more readable (especially if you get more than three tabs later), but that won't change the effectiveness or would actually make it worse.
Like ... it would be more readable to make function which will turn "off" all tabs and call it from all click handlers, although technically it means it will be needlessly removing class which is not set on one of them.
Also, if you put the matching paragraphs IDs into HTML, you can use just one function for all and use event.currentTarget to recognize which one it was called for. Again, not worth the time if you have just three tabs, but if you have more ...

Next and Previous Page Buttons for iFrame content

I made a website where there are a bunch of links that load into an iframe. There are buttons that allow the user to navigate to the next and previous links in the list. I used this to do that.
var i = 0, links = $('a').toArray();
$('.next').click(function() {
i++;
if(i === links.length){ i = links.length - 1; }
$('.frame').attr('src', links[i]);
});
//loads next link in iframe
$('.prev').click(function() {
i--;
if(i < 0){ i = 0; }
$('.frame').attr('src', links[i]);
});
//loads previous link in iframe
The problem is that if a user clicks on say the 3rd link, and then clicks the next button, it does not go to the 4th link, but rather to the 2nd link, since the click function just changes the value of i which is set to 0 by default.
To solve this I thought of creating another variable that stored the current link loaded in the iframe as such:
var current = $('.frame').contents().get(0).location.href
and then setting the value of i according to the index value of the current link as such:
var i = links.indexOf(current)
Note: I am aware that
$('.frame').contents().get(0).location.href
will cause cross-domain errors. The links I am using are from the same domain so this won't be a problem.
Sadly, this doesn't work. Any clue where I'm going wrong? Here's a fiddle.
JSFiddle
I have to use only Javascript (Jquery is fine). Please keep in mind that creating an array with the links inserted manually is not an option since there are a large number of links and more being added.
Your problem is that your i variable keep the previous index from the previous clicking on the next/prev buttons. You should fixc your code, that when the user clicks any link, i will update, as follows:
var i = 0, links = $('a').toArray();
$('.next').click(function() {
i++;
if(i === links.length){ i = links.length - 1; }
$('.frame').attr('src', links[i].href); // To get the src you must get href attribute
});
//loads next lesson in iframe
$('.prev').click(function() {
i--;
if(i < 0){ i = 0; }
$('.frame').attr('src', links[i].href); // To get the src you must get href attribute
});
//loads previous lesson in iframe
$('a').click(function() {
i = links.indexOf(this);
});
.nav {
top: 0;
z-index: 2;
width: 100%;
background: #212121;
}
.nav button {
font-size: 25px;
color: white;
padding: 10px;
width: 100px;
border-color: white;
border-radius: 8px;
background-color: #212121;
margin: 5px;
}
/*nav menu buttons*/
.frame {
height: 50vh;
width: 100%;
border: solide white 1px;
}
body {
background: #212121;
color: white;
font-family: 'Nanum Gothic', 'calibri';
margin: 5px;
}
/*body view*/
a {
padding: 5px 0px 5px 30px;
display: block;
font-size: 20px;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class= "nav">
<button class="prev">Prev</button>
<!--previous lesson button-->
<button class="next">Next</button>
<!--next lesson button-->
</div>
<iframe name="content" class="frame" src=""></iframe>
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
<li>Link 4</li>
<li>Link 5</li>
</ul>

Trouble with javascript and making it work with my exsisting code

I am using jQuery, and I'm have not used it a lot before so I'm a newbie.
I'm trying to make a slide in menu, and I found code for it. The problem, however, is when I insert it into my existing code in Sublime Text. For some reason the javascript does not work.
$(".myButton").click(function() {
var effect = 'slide';
var options = {
direction: $('.mySelect').val()
};
var duration = 500;
$('#myDiv').toggle(effect, options, duration);
});
.myButton {
font-size: 1em;
cursor: pointer;
font-weight: bold;
}
.mySelect {
padding: .2em 0;
font-size: 1em;
display: none;
}
#myDiv {
color: #fff;
background-color: pink;
border: 2px solid #000;
display: none;
text-align: justify;
padding-left: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="myButton">
run effect
</div>
<select class="mySelect">
<option value="left">Left</option>
</select>
<div id="myDiv">
<ul>
<li> episoder </li>
<li> - </li>
<li> karakterer </li>
<li> - </li>
<li> medvirkende </li>
<li> - </li>
<li> produksjon </li>
</ul>
</div>
I have set, .mySelect { display: none; }, because I don't want the user to be able to choose which side it will enter from.
You need to make sure your jQuery code runs when the DOM has been loaded. To do that you can place the <script> block just before the </body> tag. Alternatively you can place it in the <head> and wrap your JS code in a document.ready event handler, like this:
$(function() {
$(".myButton").click(function() {
var effect = 'slide';
var options = {
direction: $('.mySelect').val()
};
var duration = 500;
$('#myDiv').toggle(effect, options, duration);
});
});

How to drag and drop a div onto another

Right now my program can dynamically create rows of squares (2 rows of 12). When you double click on one of the squares, a color picker will pop up and then you can specify the color for that square.
However, I am trying to also implement a "shortcut" so that if you drag an already colored square onto another one, that new square will also be colored.
What I have done so far:
http://codepen.io/blazerix/pen/rrwPAK
var id_num = 1;
var picker = null;
$(function () {
$(document).on('click', ".repeat", function (e) {
e.preventDefault();
var $self = $(this);
var $parent = $self.parent();
if($self.hasClass("add-bottom")){
$parent.after($parent.clone(true).attr("id", "repeatable" + id_num));
id_num = id_num + 1;
//picker = null;
} else {
$parent.before($parent.clone(true).attr("id", "repeatable" + id_num));
id_num = id_num + 1;
//picker = null;
}
});
});
$(".startLEDs").draggable({
revert:true
});
I have tried to use the draggable feature from JQuery but noticed that when I try to drag the boxes, the entire div disappears. And, also I only want the boxes to be draggable and droppable, nothing else.
Any help or feedback is much appreciated!
Here is what I have so far, but due to the id attributes not being unique after cloning, it has some quirks.
Working Example: https://jsfiddle.net/Twisty/aqfn34bs/
HTML
<div class="bottomdiv">
<div class="container">
<div id="repeatable-0">
<button class="repeat add-top">Add above</button>
<ul class="repeatable">
<li class="startLEDs" id="sLED1"></li>
<li class="startLEDs" id="sLED2"></li>
<li class="startLEDs" id="sLED3"></li>
<li class="startLEDs" id="sLED4"></li>
<li class="startLEDs" id="sLED5"></li>
<li class="startLEDs" id="sLED6"></li>
<li class="startLEDs" id="sLED7"></li>
<li class="startLEDs" id="sLED8"></li>
<li class="startLEDs" id="sLED9"></li>
<li class="startLEDs" id="sLED10"></li>
<li class="startLEDs" id="sLED11"></li>
<li class="startLEDs" id="sLED12"></li>
<li class="endLEDs" id="eLED1"></li>
<li class="endLEDs" id="eLED2"></li>
<li class="endLEDs" id="eLED3"></li>
<li class="endLEDs" id="eLED4"></li>
<li class="endLEDs" id="eLED5"></li>
<li class="endLEDs" id="eLED6"></li>
<li class="endLEDs" id="eLED7"></li>
<li class="endLEDs" id="eLED8"></li>
<li class="endLEDs" id="eLED9"></li>
<li class="endLEDs" id="eLED10"></li>
<li class="endLEDs" id="eLED11"></li>
<li class="endLEDs" id="eLED12"></li>
</ul>
<div class="timeMilli">Time(ms):
<input type="text" name="time" form="form1">
</div>
<button class="repeat add-bottom">Add below</button>
</div>
</div>
</div>
I was looking at using sortable, and so I switched it to a unorganized list in my testing and then went back to draggable but still licked the list of colors versus links. I suspect you could revert back if you desired.
CSS
div.bottomdiv {
clear: both;
position: fixed;
bottom: 0;
height: 50%;
width: 100%;
font-size: 16px;
text-align: center;
overflow: auto;
}
.container {
margin-top: 60px;
float: left
}
.timeMilli {
position: relative;
display: inline-block;
}
ul.repeatable {
list-style-type: none;
margin: 0;
padding: 0;
width: 408px;
}
ul.repeatable li {
width: 30px;
height: 30px;
float: left;
margin: 1px 1px 1px 0;
background-color: #ccc;
border: 1px solid #000000;
}
b {
position: relative;
display: inline-block;
width: 30px;
height: 30px;
background-color: #ccc;
border: 1px solid #000000;
}
.repeat {
display: flex;
flex-direction: nowrap;
flex-wrap: nowrap;
}
.repeat > * {
flex: 0 0 30px;
}
Minor changes here for ul and li.
jQuery
$(function() {
function setColor(obj, r) {
obj.css('background-color', r);
obj.attr("data-color", r);
}
function makeDrag(obj) {
obj.draggable({
helper: "clone",
opacity: 0.85
});
}
function makeDrop(obj) {
obj.droppable({
accept: ".repeatable li",
drop: function(e, ui) {
if (ui.helper.data("color")) {
setColor($(this), ui.helper.data("color"));
makeDrag($(this));
}
}
});
}
$(document).on('click', ".repeat", function(e) {
e.preventDefault();
var $self = $(this);
var $parent = $self.parent();
var id_num = $("div[id^='repeatable']").length;
if ($self.hasClass("add-bottom")) {
$parent.after($parent.clone(true).attr("id", "repeatable-" + id_num));
makeDrop($("#repeatable-" + id_num + " .repeatable li"));
makeDrag($("#repeatable-" + id_num + " .repeatable li[data-color^='#']"));
} else {
$parent.before($parent.clone(true).attr("id", "repeatable-" + id_num));
makeDrop($("#repeatable-" + id_num + " .repeatable li"));
makeDrag($("#repeatable-" + id_num + " .repeatable li[data-color^='#']"));
}
});
$(".repeatable li").on("dblclick", function(e) {
$(this).spectrum({
color: "#f00",
change: function(color) {
setColor($(this), color.toHexString());
makeDrag($(this));
}
});
});
makeDrop($(".repeatable li"));
});
I didn't see a need for the id_num and picker variables in global.
Since we will be making a number of things change colors, draggable, and droppable, I created functions to do this repeatedly.
So draggable by itself would not accomplish what you needed. You need droppable to be able to action things when the dragged item is dropped. For example, it has an out and over event that you could color the box it's over while it's over it, and then revert the color once dragged out.
For now, we stick to basics: we set a color with a Double Click event, we can then drag that color to another nearby box, and the color for that box is set to the same color when dropped.
Now if we clone things before or after, things get sticky. Will look over it later, but this should get you moving forward for now.

Onhover of Navigation change to light color background

I am doing a functionality for my website, where I want a functionality as same here.
In detail: When I hover on navigation, I want to make the background light.
Do let me know if needed anything else.
I hope I understand your question. See the example below:
$(document).ready(function() {
$("ul li").mouseover(function () {
$(this).addClass('light-bg', 1000);
$('body').addClass('new-body-bg', 1000);
});
$("ul li").mouseleave(function () {
$(this).removeClass('light-bg', 1000);
$('body').removeClass('new-body-bg', 1000);
}); });
ul {
background-color: #ddd; /* Choose the color of your choice */
height: 40px;
}
li {
display: inline-block;
font-size: 18px;
padding: 10px;
line-height: 20px;
text-align: center;
margin-right: 15px;
}
.light-bg {
background-color: #fff; /* Choose the color of your choice */
line-height: 20px;
}
.new-body-bg {
background-color: #ccc; /* Choose the color of your choice */
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li> One </li>
<li> Two </li>
<li>Three </li>
<li> Four</li>
</ul>
It works perfectly on JsFiddle
See: http://jsfiddle.net/snlacks/tekokmke/1/
Without using jQuery,
you want to find all of the elements you want to do this to
then you want to loop through them
You'll apply two listeners to each one, one for entering and one for leaving.
js:
var mes = document.querySelectorAll(".me");
function changeIn(){
document.body.style.backgroundColor = "lightgray";
}
function changeOut(){
document.body.style.backgroundColor = "darkgray";
}
for(i = 0; i < mes.length; i++){
mes[i].onmouseenter = changeIn;
mes[i].onmouseleave = changeOut;
}

Categories

Resources