im newbie with vue so i have a couple of cuestions, im showing a table with some data that i get from a web-service, the thing is , that i dont always get the same amount of data, the data comes in an array, for example like this
var todos = [
{name : "dexter" , color : "orange"},
{name : "jaime", color : "green" },
{name : "stack", color : "yellow" },
{name : "overflow", color : "black" }
]
but as i say, I dont alway's get a response with 2 items in it,
right now i have to declare what item in te array im calling to, and i want to call all the items in the array to show them
var todos = [
{
name: "dexter",
color: "orange"
},
{
name: "jaime",
color: "green"
},
{
name: "stack",
color: "yellow"
},
{
name: "overflow",
color: "black"
}
]
var i = 0;
var sixthTable = new Vue({
el: '#sevenTable',
data: {
currentPage: 1,
elementsPerPage: 3,
ascending: false,
sortColumn: '',
rows: [
{
name: todos[0].name,
color: todos[0].color
},
{
name: todos[1].name,
color: todos[1].color
},
{
name: todos[2].name,
color: todos[2].color
},
{
name: todos[3].name,
color: todos[3].color
}
]
},
methods: {
"sortTable": function sortTable(col) {
if (this.sortColumn === col) {
this.ascending = !this.ascending;
} else {
this.ascending = true;
this.sortColumn = col;
}
var ascending = this.ascending;
this.rows.sort(function(a, b) {
if (a[col] > b[col]) {
return ascending ? 1 : -1
} else if (a[col] < b[col]) {
return ascending ? -1 : 1
}
return 0;
})
},
"num_pages": function num_pages() {
return Math.ceil(this.rows.length / this.elementsPerPage);
},
"get_rows": function get_rows() {
var start = (this.currentPage - 1) * this.elementsPerPage;
var end = start + this.elementsPerPage;
return this.rows.slice(start, end);
},
"change_page": function change_page(page) {
this.currentPage = page;
}
},
computed: {
"columns": function columns() {
if (this.rows.length == 0) {
return [];
}
return Object.keys(this.rows[0])
}
}
});
table {
font-family: 'Open Sans', sans-serif;
width: 750px;
border-collapse: collapse;
border: 3px solid #44475C;
margin: 10px 10px 0 10px;
}
table th {
text-transform: uppercase;
text-align: left;
background: #44475C;
color: #FFF;
cursor: pointer;
padding: 8px;
min-width: 30px;
}
table th:hover {
background: #717699;
}
table td {
text-align: left;
padding: 8px;
border-right: 2px solid #7D82A8;
}
table td:last-child {
border-right: none;
}
table tbody tr:nth-child(2n) td {
background: #00B4BB;
}
table {
font-family: 'Open Sans', sans-serif;
width: 750px;
border-collapse: collapse;
border: 3px solid #44475C;
margin: 10px 10px 0 10px;
}
table th {
text-transform: uppercase;
text-align: left;
background: #44475C;
color: #FFF;
cursor: pointer;
padding: 8px;
min-width: 30px;
}
table th:hover {
background: #007b80;
}
table td {
text-align: left;
padding: 8px;
border-right: 2px solid #7D82A8;
}
table td:last-child {
border-right: none;
}
table tbody tr:nth-child(2n) td {
background: #D4D8F9;
}
.pagination {
font-family: 'Open Sans', sans-serif;
text-align: right;
width: 750px;
padding: 8px;
}
.arrow_down {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAaCAYAAABPY4eKAAAAAXNSR0IArs4c6QAAAvlJREFUSA29Vk1PGlEUHQaiiewslpUJiyYs2yb9AyRuJGm7c0VJoFXSX9A0sSZN04ULF12YEBQDhMCuSZOm1FhTiLY2Rky0QPlQBLRUsICoIN/0PCsGyox26NC3eTNn3r3n3TvnvvsE1PkwGo3yUqkkEQqFgw2Mz7lWqwng7ztN06mxsTEv8U0Aam5u7r5EInkplUol/f391wAJCc7nEAgE9Uwmkzo4OPiJMa1Wq6cFs7Ozt0H6RqlUDmJXfPIx+qrX69Ti4mIyHA5r6Wq1egND+j+IyW6QAUoul18XiUTDNHaSyGazKcZtdgk8wqhUKh9o/OMvsVgsfHJy0iWqVrcQNRUMBnd6enqc9MjISAmRP3e73T9al3XnbWNjIw2+KY1Gc3imsNHR0YV4PP5+d3e32h3K316TySQFoX2WyWR2glzIO5fLTSD6IElLNwbqnFpbWyO/96lCoai0cZjN5kfYQAYi5H34fL6cxWIZbya9iJyAhULBHAqFVlMpfsV/fHxMeb3er+Vy+VUzeduzwWC45XA4dlD/vEXvdDrj8DvURsYEWK3WF4FA4JQP9mg0WrHZbEYmnpa0NxYgPVObm5teiLABdTQT8a6vrwdRWhOcHMzMzCiXlpb2/yV6qDttMpkeshEzRk4Wo/bfoe4X9vb2amzGl+HoXNT29vZqsVi0sK1jJScG+Xx+HGkL4Tew2TPi5zUdQQt9otPpuBk3e0TaHmMDh1zS7/f780S0zX6Yni+NnBj09fUZUfvudDrNZN+GkQbl8Xi8RLRtHzsB9Hr9nfn5+SjSeWUCXC7XPq5kw53wsNogjZNohYXL2EljstvtrAL70/mVaW8Y4OidRO1/gwgbUMvcqGmcDc9aPvD1gnTeQ+0nmaInokRj0nHh+uvIiVOtVvt2a2vLv7Ky0tL3cRTXIcpPAwMDpq6R4/JXE4vFQ5FI5CN+QTaRSFCYc8vLy1l0rge4ARe5kJ/d27kYkLXoy2Jo4C7K8CZOsEBvb+9rlUp1xNXPL7v3IDwxvPD6AAAAAElFTkSuQmCC')
}
.arrow_up {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAaCAYAAACgoey0AAAAAXNSR0IArs4c6QAAAwpJREFUSA21Vt1PUmEYP4dvkQ8JFMwtBRocWAkDbiqXrUWXzU1rrTt0bdVqXbb1tbW16C9IBUSmm27cODdneoXjputa6069qwuW6IIBIdLvdaF4OAcOiGeDc87zPs/vd57P96WpFq7p6enbGo1mjKZpeTabjU1MTCRagGnOZHFxcXxtbe1XKpUq7+zslJeXl//Mz8+Hy+Uy3RxSE9qTk5M3otFooVQqgef4Wl9f343FYoEmoISrxuNxFX5f9vb2jhn/PxUKhfLS0tIPfFifUESRUMV8Pv/M6XReRm5rTGQyGeXxeGxYe1ezeBpBOBx2rKysbO7v79d4Wy3Y2Nj4GQqFbgnhaugxwiuGJx99Pp9FLBbXxYTXvTqd7v3MzIy6riIWGxJnMpl7AwMD14xGYyMsSq1WUyQdUqn0eSPlusQIsbGrq+vl4OCgvhFQZd1utyv1en0gEolcqsi47nWJlUrlG5fLZVcoFFy2nDKSDpIWlUoVTCQSEk4lCHmJMZ2GTCbTiMVikfIZ88l7enoos9l8dXt7+z6fDicxSJUokqDX6xXcl2wCROoc0vQCWL3sNfLOSdzR0fHY4XC4tVotl40gmVwup9xuN4OQv+UyqCFGH9rg7SOGYVRcBs3IEG4J0nVnamrqOtvuBDGGgQg9+wHFcVEi4a0LNkbdd6TrPKo8ODc311mteIIYjT/a398/jK+s1jnVM0kXoufCFvq0GuiIGEVgQIhfoygM1QrteEa9dAL7ITiYCt4RMabOK5AyKKzKWtvupLcRciu8D5J0EuDDPyT/Snd39yh6VtY2NhYQSR9G79Ds7OxdskRjEyAufvb7/cPoO5Z6e1+xtVKrq6vfcFzyi/A3ZrPZ3GdNSlwgo5ekE4X2RIQGf2C1WlufFE0GBeGWYQ8YERWLxQtnUVB830MKLZfL9RHir8lkssCn2G751tZWEWe03zTKm15YWPiEiXXTYDB0Ig/t7yd8PRws4EicwWHxO4jHD8/C5HiTTqd1BwcHFozKU89origB+y/kmzgYpgOBQP4fGmUiZmJ+WNgAAAAASUVORK5CYII=')
}
.arrow {
float: right;
width: 12px;
height: 15px;
background-repeat: no-repeat;
background-size: contain;
background-position-y: bottom;
}
.number {
display: inline-block;
padding: 4px 10px;
color: #000000;
border-radius: 4px;
background: #00B4BB;
margin: 0px 5px;
cursor: pointer;
}
.number:hover,
.number.active {
background: #ccfdff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.js"></script>
<div id="sevenTable">
<table>
<thead>
<tr>
<th v-for="col in columns" v-on:click="sortTable(col)">{{col}}
<div class="arrow" v-if="col == sortColumn" v-bind:class="[ascending ? 'arrow_up' : 'arrow_down']"></div>
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in get_rows()">
<td v-for="col in columns">{{row[col]}}</td>
</tr>
</tbody>
</table>
<div class="pagination">
<div class="number" v-for="i in num_pages()" v-bind:class="[i == currentPage ? 'active' : '']" v-on:click="change_page(i)">{{i}}</div>
</div>
here is my full code i know my array its not actually a web-servce response i put in that way to "simplify" de code
thanks in advice
You can just assign the web-service-provided data right into your data like
rows: todos
Related
I saw this example on Vue.js website. This is an example of creating a reusable grid component and using it with external data.(If you want to see how it functions, here is the link : https://v2.vuejs.org/v2/examples/grid-component.html?) I have a basic understanding of how single file component works. I know <template> <script> <style> are accordingly for html, javascript and css parts. But I don't know how to make this grid component with external data fit in the skeleton. I didn't find any online tutorials about this. Please show how to fit this three files into one .vue file. Thank you.
HTML:
<!-- component template -->
<script type="text/x-template" id="grid-template">
<table>
<thead>
<tr>
<th v-for="key in columns"
#click="sortBy(key)"
:class="{ active: sortKey == key }">
{{ key | capitalize }}
<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in filteredHeroes">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
</script>
<!-- demo root element -->
<div id="demo">
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<demo-grid
:heroes="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</demo-grid>
</div>
JavaScript:
// register the grid component
Vue.component('demo-grid', {
template: '#grid-template',
props: {
heroes: Array,
columns: Array,
filterKey: String
},
data: function () {
var sortOrders = {}
this.columns.forEach(function (key) {
sortOrders[key] = 1
})
return {
sortKey: '',
sortOrders: sortOrders
}
},
computed: {
filteredHeroes: function () {
var sortKey = this.sortKey
var filterKey = this.filterKey && this.filterKey.toLowerCase()
var order = this.sortOrders[sortKey] || 1
var heroes = this.heroes
if (filterKey) {
heroes = heroes.filter(function (row) {
return Object.keys(row).some(function (key) {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortKey) {
heroes = heroes.slice().sort(function (a, b) {
a = a[sortKey]
b = b[sortKey]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return heroes
}
},
filters: {
capitalize: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
},
methods: {
sortBy: function (key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
}
}
})
// bootstrap the demo
var demo = new Vue({
el: '#demo',
data: {
searchQuery: '',
gridColumns: ['name', 'power'],
gridData: [
{ name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 }
]
}
})
CSS:
body {
font-family: Helvetica Neue, Arial, sans-serif;
font-size: 14px;
color: #444;
}
table {
border: 2px solid #42b983;
border-radius: 3px;
background-color: #fff;
}
th {
background-color: #42b983;
color: rgba(255,255,255,0.66);
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
td {
background-color: #f9f9f9;
}
th, td {
min-width: 120px;
padding: 10px 20px;
}
th.active {
color: #fff;
}
th.active .arrow {
opacity: 1;
}
.arrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
margin-left: 5px;
opacity: 0.66;
}
.arrow.asc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #fff;
}
.arrow.dsc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #fff;
}
My .vue file right now:
<template>
<!-- component template -->
<script type="text/x-template" id="grid-template">
<table>
<thead>
<tr>
<th v-for="key in columns"
#click="sortBy(key)"
:class="{ active: sortKey == key }">
{{ key | capitalize }}
<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="entry in filteredHeroes">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
</script>
<!-- demo root element -->
<div id="demo">
<form id="search">
Search <input name="query" v-model="searchQuery">
</form>
<demo-grid
:heroes="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</demo-grid>
</div>
</template>
<script>
export default {
data: function () {
var sortOrders = {}
this.columns.forEach(function (key) {
sortOrders[key] = 1
})
return {
sortKey: '',
sortOrders: sortOrders
}
},
computed: {
filteredHeroes: function () {
var sortKey = this.sortKey
var filterKey = this.filterKey && this.filterKey.toLowerCase()
var order = this.sortOrders[sortKey] || 1
var heroes = this.heroes
if (filterKey) {
heroes = heroes.filter(function (row) {
return Object.keys(row).some(function (key) {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
})
}
if (sortKey) {
heroes = heroes.slice().sort(function (a, b) {
a = a[sortKey]
b = b[sortKey]
return (a === b ? 0 : a > b ? 1 : -1) * order
})
}
return heroes
}
},
filters: {
capitalize: function (str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
},
methods: {
sortBy: function (key) {
this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1
}
}
}
</script>
<style scoped>
body {
font-family: Helvetica Neue, Arial, sans-serif;
font-size: 14px;
color: #444;
}
table {
border: 2px solid #42b983;
border-radius: 3px;
background-color: #fff;
}
th {
background-color: #42b983;
color: rgba(255,255,255,0.66);
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
td {
background-color: #f9f9f9;
}
th, td {
min-width: 120px;
padding: 10px 20px;
}
th.active {
color: #fff;
}
th.active .arrow {
opacity: 1;
}
.arrow {
display: inline-block;
vertical-align: middle;
width: 0;
height: 0;
margin-left: 5px;
opacity: 0.66;
}
.arrow.asc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid #fff;
}
.arrow.dsc {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #fff;
}
</style>
I don't know what to do with the javascript file in <script>, obviously I can't just copy it and paste. But I am confused about what changes I should make.
Assuming you've set up your build pipeline properly your SFC doesn't look correct. Taking a deeper look into the guide about SFC you have to wrap each part of your code into its own block, meaning.
HTML (template)
JavaSCript (script)
CSS (style)
Your SFC would then have to look like this.
<template>
<!-- HTML markup here -->
</template>
<script>
// JavaScript code here
</script>
<style>
/* CSS stylings here */
</style>
In your very example, you're mixing the template and script block which is not supposed to look like that.
I have these elements created inside a "querySelector('ul'). It's working property.
I want the "blue-Save" button to have the same function as the "yellow-Save".
But the Blue-save button was created in the HTML file, and the Yellow-Save button was created in JavaScript to listen to an event from the "querySelector('ul').
Is there anyway I can just link the Blue-Save to react as if I was clicking in the Yellow-Save?
(I'm sorry if I didn't explain it property or If it seems too confused, this is my first application, It doesn't seems too organized but I'm focused in making things work before dive in 'well developed apps').
Thank You Everyone!
var todoList = {
todos: [],
addTodo: function (todoText) {
this.todos.push({
todoText: todoText,
/*the name of the property (even if it is the same name as the parameter) never change. Only the value, which follows in this case is following the parameter*/
completed: false
});
},
changeTodo: function (position, todoText) {
this.todos[position].todoText = todoText;
},
deleteTodo: function (position) {
this.todos.splice(position, 1);
},
toggleCompleted: function (position) {
var todo = this.todos[position];
todo.completed = !todo.completed;
/*Here we flip the boolean to his oposite value. if todo.completed is equal false, so changes it to true, and so on. */
},
toggleAll: function () {
// recording the number of todos and completed todos
var totalTodos = this.todos.length;
var completedTodos = 0;
// get the number of completed todos.
this.todos.forEach(function (todo) {
if (todo.completed === true) {
completedTodos++;
}
});
this.todos.forEach(function (todo) {
// Case 1: If everything is true, make everything.
if (completedTodos === totalTodos) {
todo.completed = false;
// Case 2: Otherwise, make everything true.
} else {
todo.completed = true;
}
});
}
};
var handlers = {
addTodo: function () {
var addTodoTextInput = document.getElementById('add-todo-text-input');
todoList.addTodo(addTodoTextInput.value);
addTodoTextInput.value = '';
view.displayTodos();
},
changeTodo: function (position) {
var changeTodoTextInput = document.getElementById('change-todo-text-input');
todoList.changeTodo(position, changeTodoTextInput.value);
changeTodoTextInput.value = '';
view.displayTodos();
},
deleteTodo: function (position) {
todoList.deleteTodo(position);
view.displayTodos();
},
toggleCompleted: function (position) {
todoList.toggleCompleted(position);
view.displayTodos();
},
toggleAllButton: function () {
todoList.toggleAll();
view.displayTodos();
}
};
var view = {
displayTodos: function () {
var todosUl = document.querySelector('ul');
todosUl.innerHTML = '';
todoList.todos.forEach(function (todo, position) {
var todoLi = document.createElement('li');
var todoTextWithCompletion = '';
if (todo.completed === true) {
todoTextWithCompletion = todo.todoText;
todoLi.classList.add('item-completed');
} else {
todoTextWithCompletion = todo.todoText;
}
todoLi.id = position;
todoLi.textContent = todoTextWithCompletion;
todoLi.appendChild(this.createEditButton());
todoLi.appendChild(this.createToggleButton());
todoLi.appendChild(this.createDeleteButton());
todoLi.appendChild(this.createSaveButton());
todosUl.appendChild(todoLi);
}, this);
},
createDeleteButton: function () {
var deleteButton = document.createElement('button');
deleteButton.textContent = '\u2715';
deleteButton.className = 'delete-button';
return deleteButton;
},
createToggleButton: function () {
var toggleButton = document.createElement('button');
toggleButton.textContent = '\u2713';
toggleButton.className = 'toggle-button';
return toggleButton;
},
createSaveButton: function () {
var saveButton = document.createElement('button');
saveButton.textContent = 'Save';
saveButton.className = 'save-button';
return saveButton;
},
createEditButton: function () {
var editButton = document.createElement('button');
editButton.textContent = '\u270E';
editButton.className = 'edit-button';
return editButton;
},
setUpEventListeners: function () {
var todosUl = document.querySelector('ul');
todosUl.addEventListener('click', function (event) {
// Get the element that was clicked on.
var elementClicked = event.target;
// Check if elementClicked is a delete button.
if (elementClicked.className === 'delete-button') {
handlers.deleteTodo(parseInt(elementClicked.parentNode.id));
} else if (elementClicked.className === 'toggle-button') {
handlers.toggleCompleted(parseInt(elementClicked.parentNode.id));
} else if (elementClicked.className === 'save-button') {
handlers.changeTodo(parseInt(elementClicked.parentNode.id));
} else if (elementClicked.className === 'edit-button') {
}
});
}
};
view.setUpEventListeners();
body {
font-family: Helvetica, sans-serif;
font-size: 25px;
background: rgb(236, 236, 236);
}
h1 {
color: rgb(255, 255, 255);
text-align: center;
font-family: Helvetica, sans-serif;
font-size: 50px;
text-transform: uppercase;
background: rgb(48, 48, 48);
position: relative;
}
.container {
margin: auto;
width: 50%;
}
ul {
list-style: none;
padding:0px;
margin: 10px;
}
.add-button {
background-color: rgb(68, 165, 230); /* Blue */
border: none;
color: white;
margin:auto;
padding: 15px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
border-radius: 5px;
width:20%;
}
.add-button:hover {
background-color :rgb(53, 127, 177); /* Green */
color: white;
}
.save-change-button {
background-color: rgb(68, 165, 230); /* Blue */
border: none;
color: white;
margin:auto;
padding: 15px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
border-radius: 5px;
width:20%;
}
.save-change-button:hover {
background-color :rgb(53, 127, 177); /* Green */
color: white;
}
.toggle-all-button {
background-color: rgb(38, 156, 38); /* Green */
border: none;
color: white;
margin: 10px 0;
padding: 15px 32px;
text-align: center;
text-decoration: none;
font-size: 16px;
border-radius: 5px;
width: 100%;
}
.toggle-all-button:hover {
background-color : rgb(36, 114, 36); /* Green */
color: white;
}
.edit-button {
background-color: rgb(219, 208, 50); /* Green */
border: none;
color: white;
padding: 0;
text-align: center;
text-decoration: none;
font-size: 18px;
border-radius: 5px;
width: 25px;
height: 25px;
margin: 0 0 0 10px;
}
.edit-button:hover {
background-color: rgb(185, 175, 26); /* Green */
color: white;
}
.toggle-button {
background-color: rgb(38, 156, 38); /* Green */
border: none;
color: white;
padding: 0;
text-align: center;
text-decoration: none;
font-size: 18px;
border-radius: 5px;
width: 25px;
height: 25px;
margin: 0 0 0 10px;
}
.toggle-button:hover {
background-color: rgb(36, 114, 36); /* Green */
color: white;
}
.delete-button {
background-color: rgb(168, 44, 44); /* Green */
border: none;
color: white;
margin: 0 0 0 10px;
padding: 0;
text-align: center;
text-decoration: none;
font-size: 18px;
border-radius: 5px;
width: 25px;
height: 25px;
}
.delete-button:hover {
background-color :rgb(128, 31, 31); /* Green */
color: white;
}
.save-button {
background-color: rgb(219, 208, 50); /* Green */
border: none;
color: white;
padding: 0;
text-align: center;
text-decoration: none;
font-size: 18px;
border-radius: 5px;
width: 55px;
height: 25px;
margin: 0 10px;
}
.save-button:hover {
background-color :rgb(185, 175, 26); /* Green */
color: white;
}
.add-input {
margin: 10px 0;
padding: 6px 0;
text-align: center;
font-family: Arial, Helvetica, sans-serif;
font-size: 18px;
width: 78%;
}
.edit-input {
margin: 10px 0;
padding: 6px 0;
text-align: center;
font-family: Arial, Helvetica, sans-serif;
font-size: 18px;
width: 78%;
}
.item-completed {
text-decoration: line-through;
}
::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
color: rgba(209, 209, 209, 0.555);
font-family: 'Times New Roman', Times, serif;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Todo List</title>
<link href="index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="container">
<h1>Todo List</h1>
<div>
<input id="add-todo-text-input" class="add-input" placeholder="Add a New Todo to Your List" type="text">
<button class="add-button" onclick="handlers.addTodo()">Add</button>
</div>
<ul>
</ul>
<div id="edit-todo"">
<input id="change-todo-text-input" class="edit-input" placeholder="Add the Changes Your Want to Make" type="text">
<button class="save-change-button">Save</button>
</div>
<div id="toggle-all"">
<button class="toggle-all-button" onclick="handlers.toggleAllButton()">Toggle All</button>
</div>
</div>
<script src="index.js"></script>
</body>
</html>
You can add a common class to both blue and yellow buttons, and attach a click event by the class name like below
$(".custom_save_button").on("click", function() {
//your code
});
Since you are creating that button, sharing the same class won't just work as expected, instead use delegated events which allow you to attach events on new elements added to the DOM
Add a common class for both and then add an event listener for that class using event delegation.
For this example Let's suppose you chose the class name: stack_class
//I added this function for you to be able to know if an element has a class
function hasClass( target, className ) {
return new RegExp('(\\s|^)' + className + '(\\s|$)').test(target.className);
}
//you could change body to the closest parent's selector both buttons share
document.querySelector('body').addEventListener('click', function(event) {
var clickedElement = event.target;
if(hasClass(clickedElement, "stack_class")) {
//create your functionality for both buttons here
}
});
If you assign a common class to both buttons, then you can add the same event listener to all buttons that have that class. Here is a simple example:
var saveButton = document.createElement('button');
saveButton.textContent = 'Save';
saveButton.className = 'save-button yellow-button';
body = document.querySelector('body');
body.appendChild(saveButton);
buttons = document.getElementsByClassName('save-button');
for (var i = 0; i < buttons.length; i += 1) {
buttons[i].addEventListener('click', function (event) {
alert('Hello!');
});
}
.yellow-button {
background: #ffff00;
}
.blue-button {
background: #0000ff;
}
<button class="save-button blue-button">Save</button>
I have the following form for lyrics upload. I've changed the design of the form a little bit, and now facing a weird problem.
I've created a fake-datalist using JS. On input focus, a fake-datalist (an ul element) is appended next to the input element. Its position is set to absolute so it shouldn't disrupt the flow of the document when it appears. However, it does. I can't seem to identify the problem. Once the datalist appears, the div next to the table gets pushed down. Table width isn't changing when the datalist appears, so it's not squizing the div and pushing it down.
Code Pen
var artists = [{"artist":"3 Doors Down"},{"artist":"5 Seconds of Summer"},{"artist":"Adele"},{"artist":"Alicia Keys"},{"artist":"Amanda Abizaid"},{"artist":"Avril Lavigne"}];
var albums = [{"album":"The Better Life","year":"2000","cover":"3_doors_down_2000_the_better_life.jpg"},{"album":"Away from the Sun","year":"2002","cover":"3_doors_down_2002_away_from_the_sun.jpg"},{"album":"Seventeen Days","year":"2005","cover":"3_doors_down_2005_seventeen_days.jpg"},{"album":"3 Doors Down","year":"2008","cover":"3_doors_down_2008_3_doors_down.jpg"},{"album":"Time of My Life","year":"2011","cover":"3_doors_down_2011_time_of_my_life.jpg"}];
var songs = [{"song":"Kryptonite","track_no":"1"},{"song":"Duck and Run","track_no":"3"},{"song":"Be Like That","track_no":"5"},{"song":"So I Need You","track_no":"11"}];
function datalist(element) {
return new datalist.prototype.init(element);
}
datalist.prototype = {
init: function(element) {
if (!element) {
this.element = document.createElement("ul");
this.element.classList.add("datalist");;
this.hide();
} else {
this.element = element;
}
},
update: function(queryElement) {
this.clear();
var lookUpArray = queryElement.name + "s";
var results = this.search(window[lookUpArray], queryElement.value, queryElement.name);
for (var i = 0; i < results.length; i++) {
var li = document.createElement("li");
var value = results[i][queryElement.name];
switch (queryElement.name) {
case "album":
li.setAttribute("data-year", results[i].year);
break;
case "song":
li.setAttribute("data-track_no", results[i].track_no);
break;
}
if (queryElement.value != "") {
var re = new RegExp(queryElement.value, "gi");
value = value.replace(re, "<span class=\"highlight\">" + "$&" + "</span>");
}
li.innerHTML = value;
this.element.appendChild(li);
}
return results.length;
},
search: function(lookUpArray, string, queryType) {
var results = [];
for (var i = 0; i < lookUpArray.length; i++) {
if (lookUpArray[i][queryType].toLowerCase().search(string.toLowerCase()) != -1) {
results.push(lookUpArray[i]);
}
}
return results;
},
clear: function() {
this.element.innerHTML = "";
},
hide: function() {
this.element.style.display = "none";
},
show: function() {
this.element.style.display = "";
},
remove: function() {
this.element.parentElement.removeChild(this.element);
},
for: function(sibling) {
sibling.parentElement.appendChild(this.element);
this.hide();
},
};
datalist.prototype.init.prototype = datalist.prototype;
var lastVisitedInput = null;
$("#lyrics-form").on("focus", "input.datalist-input", function() {
if (this.parentElement.children.length == 1) {
this.parentElement.appendChild(datalist().element);
}
if (lastVisitedInput) {
datalist(lastVisitedInput.nextElementSibling).hide();
}
lastVisitedInput = this;
if (datalist(this.nextElementSibling).update(this)) {
datalist(this.nextElementSibling).show();
} else {
datalist(this.nextElementSibling).hide();
}
});
$(document).on("click", function(e) {
if (lastVisitedInput) {
var exceptions = getExceptions(lastVisitedInput);
if (!contains(exceptions, e.target)) {
datalist(lastVisitedInput.nextElementSibling).remove();
lastVisitedInput = null;
}
}
});
$("#lyrics-form").on("input", "input.datalist-input", function() {
if (datalist(this.nextElementSibling).update(this)) {
datalist(this.nextElementSibling).show();
} else {
datalist(this.nextElementSibling).hide();
}
});
$("#lyrics-form").on("click", "li", function() {
this.parentElement.previousElementSibling.value = this.innerText;
$(this.parentElement.previousElementSibling).trigger("input");
});
function getRecord(input) {
var lookUpArray = window[input.name + "s"];
for (var i = 0; i < lookUpArray.length; i++) {
if (input.value == lookUpArray[i][input.name]) {
return lookUpArray[i];
}
}
return false;
}
function getExceptions(input) {
var exceptions = [
input,
input.nextElementSibling,
];
for (var i = 0; i < input.nextElementSibling.children.length; i++) {
exceptions.push(input.nextElementSibling.children[i]);
}
return exceptions;
}
function contains(array, item) {
for (var i = 0; i < array.length; i++) {
if (array[i] === item) {
return true;
}
}
return false;
}
* { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } *, *:before, *:after { box-sizing: inherit; } body { line-height: 1.5; font-family: sans-serif; } input[type="button"], input[type="submit"] { cursor: pointer; } textarea, input[type="text"], input[type="search"], input[type="number"], input[type="password"] { border: 1px solid rgba(0,0,0,.2); padding: 4px; margin: 1px; } table { border-collapse: collapse; border-spacing: 0; }
body {
background-color: rgb(230, 230, 230);
font-family: Arial, sans-serif;
font-size: 14px;
color: rgba(0, 0, 0, .8);
box-sizing: border-box;
}
#main {
height: 500px;
background: white;
box-shadow: 0 0 2px rgba(0, 0, 0, .1), 0 2px 2px rgba(0, 0, 0, .1);
margin: 20px auto;
display: table;
padding: 20px;
}
#songInput {
overflow: auto;
}
#songTable td {
position: relative;
}
#songTable,
#coverDiv {
float: left;
}
#coverDiv {
margin-left: 20px;
}
#artist,
#album,
#song {
width: 250px;
}
#artist {
width: 300px;
width: 100%;
}
#year,
#track_no {
width: 70px;
}
#songTable td {
padding-bottom: 20px;
}
#songTable td:first-child {
padding-right: 10px;
}
#songTable .int-input {
padding-left: 20px;
padding-right: 10px;
}
#coverDiv > * {
display: block;
}
#coverDiv img {
width: 137px;
height: 137px;
border: 1px solid rgba(0, 0, 0, .2);
margin: 1px;
}
#coverUpload {
margin: 1px;
margin-top: 10px;
width: 250px;
}
#lyricsBox {
width: 100%;
height: 400px;
margin-top: 15px;
}
#submit {
width: 100%;
margin-top: 15px;
}
.datalist {
border: 1px solid silver;
box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
position: absolute;
top: 32px;
left: 1px;
background: white;
padding: 5px;
max-height: 195px;
width: 180px;
width: 100%;
overflow-y: scroll;
z-index: 1000;
}
.datalist li {
padding: 2px 5px;
cursor: default;
}
.datalist li:hover {
background: rgba(0, 0, 0, .05);
color: black;
}
.datalist .highlight {
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
<form action="addlyrics.php" id="lyrics-form" method="post" autocomplete="off" enctype="multipart/form-data">
<div id="songInput">
<table id="songTable">
<tr>
<td>Artist</td>
<td colspan="3">
<input type="search" name="artist" id="artist" class="datalist-input" placeholder="Artist" required />
</td>
</tr>
<tr>
<td>Album</td>
<td>
<input type="search" name="album" id="album" class="datalist-input" placeholder="Album" required />
</td>
<td class="int-input">Year</td>
<td>
<input type="number" name="year" id="year" class="input-num" placeholder="Year" required />
</td>
</tr>
<tr>
<td>Song</td>
<td>
<input type="search" name="song" id="song" class="datalist-input" placeholder="Name" required />
</td>
<td class="int-input">#</td>
<td>
<input type="number" name="track_no" id="track_no" class="input-num" placeholder="ID" required />
</td>
</tr>
</table>
<div id="coverDiv">
<img src="covers/blank.gif" id="cover" />
<input type="file" name="cover" id="coverUpload" accept="image/*" />
</div>
</div>
<textarea name="lyrics" placeholder="Lyrics" id="lyricsBox" /></textarea>
<input type="submit" id="submit" class="button" />
</form>
</div>
Removing overflow: auto; from #songInput, the parent element of the table and the div, solved the problem. Although, I don't understand why overflow: auto; on the parent would push the div down. Dynamically added ul.datalist's position is set to absolute, and when it appears, the only thing it might do is extend the height of the table, which shouldn't effect the div at the right.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
can anyone help me with my js selector functions? The problem is the color and size indicator numbers showing do not default back to "blank" after mouseout. I want the indicator number to disappear after mouseout if a mouseclick never happens over the function. Can anyone help? My demo http://jsfiddle.net/jwjxr2oh/3/
Thanks
The problem is when sizeIndicator.data('selected') is not present sizeIndicator.data('selected') will return undefined, then the text() method will act like a getter method and will not update the value to blank so
$(function() {
var colorsList = $('#colors');
var sizesList = $('#sizes');
var colorIndicator = $('#color_indicator');
var sizeIndicator = $('#size_indicator');
var colors = [{
id: 1,
name: 'Dark Blue Denim',
color: '#0B3861'
}, {
id: 2,
name: 'Light Blue Denim',
color: '#81BEF7'
}];
var sizes = [{
id: 1,
name: '26'
}, {
id: 2,
name: '28'
}, {
id: 2,
name: '30'
}, {
id: 3,
name: '32'
}];
$.each(colors, function() {
colorsList.append(
$('<li>').attr({
id: 'color_' + this.id
})
.data('info', this)
.append(
$('<div>').css('background-color', this.color)));
});
$.each(sizes, function() {
sizesList.append(
$('<li>').attr({
id: 'size_' + this.id
})
.data('info', this)
.text(this.name)
.addClass('text'));
});
colorsList.selectable({
selected: function(event, ui) {
var total = $(ui.selected).siblings('li.ui-selected').length + 1;
if (total > 1) {
$(ui.selected).removeClass('ui-selected');
} else {
var color = $(ui.selected).data('info').name;
colorIndicator.data('selected', color).text(color);
}
}
});
sizesList.selectable({
selected: function(event, ui) {
var total = $(ui.selected).siblings('li.ui-selected').length + 1;
if (total > 1) {
$(ui.selected).removeClass('ui-selected');
} else {
var size = $(ui.selected).data('info').name;
sizeIndicator.data('selected', size).text(size);
}
}
});
sizesList.children('li').hover(function() {
sizeIndicator.text($(this).data('info').name);
}, function() {
console.log(sizeIndicator)
sizeIndicator.text(sizeIndicator.data('selected') || '');
});
colorsList.children('li').hover(function() {
colorIndicator.text($(this).data('info').name);
}, function() {
colorIndicator.text(colorIndicator.data('selected') || '');
});
$('#show').click(function() {
var color = colorsList.find('li.ui-selected').eq(0).data('info');
var size = sizesList.find('li.ui-selected').eq(0).data('info');
if (typeof color != 'undefined') {
alert('Color selected: ' + color.name + ', id: ' + color.id);
}
if (typeof size != 'undefined') {
alert('Size selected: ' + size.name + ', id: ' + size.id);
}
});
});
.selectable li.ui-selecting {} .selectable li.ui-selected {
border: 1px solid #000;
}
.selectable {
list-style-type: none;
margin: 0;
padding: 0;
}
.selectable li {
border: 1px solid #fff;
margin: 3px;
padding: 3px;
float: left;
text-align: center;
}
.selectable li:hover {
border: 1px solid #000;
margin: 3px;
padding: 3px;
cursor: pointer;
cursor: hand;
float: left;
text-align: center;
}
.selectable li div {
border: 1px solid #000;
padding: 5px;
margin: 3px;
width: 20px;
height: 20px;
}
.selectable li.text {
color: #000;
padding: .5em .5em;
margin: .5em 1em;
font-size: 1.5em;
border-radius: 50%;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
line-height: 1em;
}
.selectable li.text:hover {
cursor: pointer;
cursor: hand;
color: #000;
font-weight: normal;
border: 1px solid #000;
}
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.js"></script>
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/redmond/jquery-ui.css">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.js"></script>
<input type='button' id='show' value='What is selected?'>
<table width="595" height="172" border="0">
<tr>
<td width="421" height="40" align="center" valign="middle">Size: <span id="size_indicator"></span>
</td>
<td width="164">Color: <span id="color_indicator"></span></td>
</tr>
<tr>
<td height="103">
<ol id="sizes" class='selectable'></ol>
</td>
<td>
<ol id="colors" class='selectable'></ol>
</td>
</tr>
</table>
I must be an idiot. I am using the Typeahead.js plugin. I am trying to use custom templates for suggestions. While my custom templates appear, I cannot use the arrow-keys to actually select an item. If I hover over an item, the selection doesn't get highlighted either. I thought it might be just a styling issue. However, if 3 suggestions appear, and I click the down arrow twice, then enter, my selected option does not appear in the text box. Alternatively, if I choose an option with my mouse, the option does not appear in the box.
What am I doing wrong? Currently, I have the following:
var suggestions = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/api/suggests?querytext=%QUERY',
filter: function(results) {
return $.map(results.Results, function(suggestion) {
return suggestion;
});
}
});
suggestions.initialize();
$(document).ready(function() {
$('input.typeahead').typeahead(
{ minLength: 3 },
{
name: 'suggestions',
source: suggestions.ttAdapter(),
templates: {
suggestion: function(data) {
var str = '';
if (data.Type === 'Customer') {
str += '<i class="icon-1"></i>';
} else if (data.Type === 'Product') {
str += '<i class="icon-2"></i>';
}
str += '<div>' + data.Name + '</div>';
return str;
}
}
}
);
});
The suggestions popup. The results come from the following JSON:
{
"Results":[
{
"Type":"Customer",
"Id":5,
"Name":"Bill",
"LastUpdated":"01-01-2015"
},
{
"Type":"Customer",
"Id":135,
"Name":"Billows",
"LastUpdated":"01-02-2015",
},
{
"Type":"Product",
"Id":241,
"Name":"Bill Check",
"LastUpdate":"01-04-2015"
}
],
"TotalResults":3,
"TotalCustomers":2,
"TotalProducts":1
}
How do I a) Apply a highlight to an item when a use hovers over an item with the mouse or uses the arrow keys to get to it b) Put the select item's Name value in the input box when the suggestion is selected?
Thanks!
Try
$(function () {
var suggestions = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/api/suggests?querytext=%QUERY',
filter: function(results) {
return $.map(results.Results, function(suggestion) {
return {value:suggestion.Name, suggest:suggestion};
});
}
});
suggestions.initialize();
$("#bloodhound .typeahead").typeahead({
minLength: 3,
hint: true,
highlight: true
}, {
name: 'suggestions',
displayKey: 'value',
templates: {
suggestion: function(data) {
var str = '';
if (data.suggest.Type === 'Customer') {
str += '<i class="icon-1">' + data.suggest.Type + '</i>';
} else if (data.suggest.Type === 'Product') {
str += '<i class="icon-2">' + data.suggest.Type + '</i>';
}
str += '<div>' + data.value + '</div>';
return str;
}
},
source: suggestions.ttAdapter()
});
})
$(function () {
var data = {
"Results":[
{
"Type":"Customer",
"Id":5,
"Name":"Bill",
"LastUpdated":"01-01-2015"
},
{
"Type":"Customer",
"Id":135,
"Name":"Billows",
"LastUpdated":"01-02-2015",
},
{
"Type":"Product",
"Id":241,
"Name":"Bill Check",
"LastUpdate":"01-04-2015"
}
],
"TotalResults":3,
"TotalCustomers":2,
"TotalProducts":1
};
var suggestions = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
local: $.map(data.Results, function(d) {
return {value:d.Name, suggest:d}
})
});
suggestions.initialize();
$("#bloodhound .typeahead").typeahead({
minLength: 3,
hint: true,
highlight: true
}, {
name: 'suggestions',
displayKey: 'value',
templates: {
suggestion: function(data) {
var str = '';
if (data.suggest.Type === 'Customer') {
str += '<i class="icon-1">'+data.suggest.Type+'</i>';
} else if (data.suggest.Type === 'Product') {
str += '<i class="icon-2">'+data.suggest.Type+'</i>';
}
str += '<div>' + data.value + '</div>';
return str;
}
},
source: suggestions.ttAdapter()
});
})
#font-face {
font-family:"Prociono";
src: url("../font/Prociono-Regular-webfont.ttf");
}
html {
overflow-y: scroll;
}
.container {
margin: 0 auto;
max-width: 750px;
text-align: center;
}
.tt-dropdown-menu, .gist {
text-align: left;
}
html {
color: #333333;
font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 18px;
line-height: 1.2;
}
.title, .example-name {
font-family: Prociono;
}
p {
margin: 0 0 10px;
}
.title {
font-size: 64px;
margin: 20px 0 0;
}
.example {
padding: 30px 0;
}
.example-name {
font-size: 32px;
margin: 20px 0;
}
.demo {
margin: 50px 0;
position: relative;
}
.typeahead, .tt-query, .tt-hint {
border: 2px solid #CCCCCC;
border-radius: 8px 8px 8px 8px;
font-size: 24px;
height: 30px;
line-height: 30px;
outline: medium none;
padding: 8px 12px;
width: 396px;
}
.typeahead {
background-color: #FFFFFF;
}
.typeahead:focus {
border: 2px solid #0097CF;
}
.tt-query {
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset;
}
.tt-hint {
color: #999999;
}
.tt-dropdown-menu {
background-color: #FFFFFF;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 8px 8px 8px 8px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
margin-top: 12px;
padding: 8px 0;
width: 422px;
}
.tt-suggestion {
font-size: 18px;
line-height: 24px;
padding: 3px 20px;
}
.tt-suggestion.tt-cursor {
background-color: #0097CF;
color: #FFFFFF;
}
.tt-suggestion p {
margin: 0;
}
.gist {
font-size: 14px;
}
.example-twitter-oss .tt-suggestion {
padding: 8px 20px;
}
.example-twitter-oss .tt-suggestion + .tt-suggestion {
border-top: 1px solid #CCCCCC;
}
.example-twitter-oss .repo-language {
float: right;
font-style: italic;
}
.example-twitter-oss .repo-name {
font-weight: bold;
}
.example-twitter-oss .repo-description {
font-size: 14px;
}
.example-sports .league-name {
border-bottom: 1px solid #CCCCCC;
margin: 0 20px 5px;
padding: 3px 0;
}
.example-arabic .tt-dropdown-menu {
text-align: right;
}
[class|=icon] {
color:orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>
<div id="bloodhound">
<input class="typeahead" type="text" placeholder="Customers and Products" />
</div>
Have you tried adding a valueKey to your function pointing it to name? See SO answer here:
Typeahead.js does give me suggestions but doesn't select them