I am starting off with html5 and not sure if I'm doing anything wrong in here.
What I'm trying to achieve is create a portfolio of items and when one is selected, pass the selected item to a function to process it.
I have put up a simple jsfiddle in here https://jsfiddle.net/rohannayak90/9u9rtf84/
data = JSON.parse('{"error":false,"items":[{"id":1,"name":"John"},{"id":2,"name":"Jordan"}]}');
jQuery.each(data.items, function(counter, item) {
//console.log(item);
h = '' + item.name + '</br>';
$('#portfolio').append(h);
});
function generateCallBack(argItem) {
return function() {
itemSelected(argItem);
};
};
function itemSelected(argItem) {
//console.log(argItem.name);
alert(argItem.name);
};
Thanks in advance.
You might want to do something like this (you can change arrow functions to normal ones if that's a problem):
const data = JSON.parse('{"error":false,"items":[{"id":1,"name":"John"},{"id":2,"name":"Jordan"}]}');
const array = data.items;
const length = array.length;
let string = '';
data.items.map(item => {
string += '' + item.name + '</br>';
});
const portfolio = document.getElementById('portfolio');
portfolio.innerHTML += string;
portfolio.addEventListener('click', function(event) {
if(event.target.tagName !== 'A') return;
let i = -1;
while(++i < length) {
if(array[i].name === event.target.textContent) {
return alert(JSON.stringify(array[i].name)); // return ends the function and thus the loop too.
}
}
});
Plain Javascript. No jQuery crap.
I would recommend to to create HTML using jQuery() and use .data(key, value) to store arbitrary value with element.
Additionally learn Event Delegation using .on() delegated-events approach, when generating elements dynamically.
General Syntax
$(staticParentContainer).on('event','selector',callback_function)
Snippet for your solution
$(function() {
var data = JSON.parse('{"error":false,"items":[{"id":1,"name":"John"},{"id":2,"name":"Jordan"}]}');
jQuery.each(data.items, function(counter, item) {
//console.log(item);
$('<a />', {
href: '#',
text: item.name,
'class': 'myClass'
})
.data('item', item)
.add('<br />')
.appendTo('#portfolio');
});
$('#portfolio').on('click', 'a.myClass', function() {
console.log($(this).data('item'));
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="showcase" class="container">
<ul id="portfolio" class="grid">
</ul>
</div>
Related
currently i'm starting with Ember, and i'm loving it! I'm with some difficulties, especially when it comes to components.
For you to understand, I'm going through old code to Ember, and I would like to turn this code into a Component, but I do not know actually how to start, since I do not know how to catch the button being clicked, and I also realized that Ember has several helpers, maybe I do not need any of this giant code to do what I want.
This is the old code result: http://codepen.io/anon/pen/WQjobV?editors=110
var eventObj = {};
var eventInstances = {};
var actual;
var others;
var clicked;
var createEventInstance = function (obj) {
for (var key in obj) {
eventInstances[key] = new Event(obj[key]);
}
};
var returnStyle = function (inCommon) {
var $inCommon = inCommon;
$inCommon.css({
width: '342.4px',
minWidth: '342.4px'
});
$inCommon.find('.cta').removeClass('hidden');
$inCommon.find('.event-close').removeClass('inline');
$inCommon.find('.event-info_list').removeClass('inline');
$inCommon.removeClass('hidden');
$inCommon.find('.expanded').slideUp();
$inCommon.find('.expanded').slideUp();
$inCommon.find('.event-arrow').remove();
$inCommon.find('h2').find('ul').remove('ul');
};
var Event = function (id) {
this.id = id;
};
Event.prototype.expandForm = function () {
actual.css('width', '100%');
actual.find('.event-info_list').addClass('inline');
actual.find('.expanded').slideDown().css('display', 'block');
actual.find('.event-close').addClass('inline');
};
Event.prototype.close = function () {
returnStyle(actual);
returnStyle(others);
};
Event.prototype.hideElements = function () {
clicked.addClass('hidden');
others.addClass('hidden');
};
Event.prototype.maskPhone = function () {
$('[name$=phone]').mask('(99) 99999-9999', {
placeholder: '(00) 0000-0000'
});
};
$('.submit-form').on('click', function (e) {
e.preventDefault();
var id = '.' + $(this).data('id');
var name = $(id).children('#person-name').val();
var email = $(id).children('#person-email').val();
var guests = $(id).children('#person-obs.guests').val();
var phone = $(id).children('#person-phone').val();
var participants = $(id).children('#booking-participants').val();
if (name === '' || email === '' || phone === '' || participants === '' || guests === '') {
alert('Preencha os campos obrigatórios.');
} else {
$(id).submit();
}
});
Event.prototype.createDropDown = function () {
actual.find('h2').addClass('event-change')
.append('<span class="event-arrow" aria-hidden="true">â–¼</span>')
.append(function () {
var self = $(this);
var list = '<ul class="dropdown hidden">';
$('.event').each(function (index) {
if ($(this).find('h2')[0] != self[0]) {
list += '<li data-index="' + index + '">' + $(this).find('h2').text() + '</li>';
}
});
return list;
}).click(function () {
if ($(this).attr('data-expanded') == true) {
$(this).find('ul').toggleClass('hidden');
$(this).attr('data-expanded', false);
} else {
$(this).find('ul').toggleClass('hidden');
$(this).attr('data-expanded', true);
}
}).find('li').click(function (e) {
e.stopPropagation();
actual.find('.event-info_list').removeClass('inline');
actual.find('h2').attr('data-expanded', false);
actual.find('h2').removeClass('event-change');
actual.find('.expanded').slideUp().css('display', 'inline-block');
others.removeClass('hidden');
actual.find('.cta').removeClass('hidden');
actual.find('h2').find('.event-arrow').remove();
actual.find('h2').off('click');
actual.find('h2').find('ul').remove('ul');
$($('.event')[$(this).attr('data-index')]).find('.cta').trigger('click');
});
};
Event.prototype.open = function () {
actual = $('[data-id="' + this.id + '"]');
others = $('.event').not(actual);
clicked = actual.find('.cta');
this.hideElements();
this.expandForm();
this.createDropDown();
this.maskPhone();
};
$('.event').each(function (i, event) {
var prop = 'id' + $(event).data('id');
var value = $(event).data('id');
eventObj[prop] = value;
});
createEventInstance(eventObj);
Basically i have this boxes, which box represent one booking in some event (will be populate by the server). When the user clicks in one box, this boxes expands and the other disappear. But than a dropbox will be created with the other boxes, so the user can navigate in the events by this dropdown.
I didn't do much with Ember, i transform the "events" div into a component with the name "BookingBoxComponent" and two actions:
SiteApp.BookingBoxComponent = Ember.Component.extend({
actions:
open: function() {
// HOW COULD I ACCESS THE CLICKED BUTTON HERE?
},
close: function() {
}
});
As you can see, i put two actions, one for opening the box and other for closing, should i just put the logic in both, or i can improve this like a Ember way?
I don't know if i am asking to much here, so if i am, at least i would like to know how to access the button clicked in the open method, i was trying passing as a parameter, like:
<button {{action 'open' this}}></button>
But didn't work.
I could offer 50 of my points to someone who help transform the old cold in a Ember way code.
Thanks.
The event object will be passed with every action as the last parameter, so when you specified this you were actually passing whatever object has context in that block. In your open function, do not pass this and do
open: function(event) {
// event.currentTarget would be the button
}
And now you can do something like event.currentTarget or event.target
I was using the following code without Backbone.js and it was working - preventing the ghost images from appearing when trying to drag the image:
$(document).ready(function() {
$('img').attr('draggable', false);
document.getElementsByTagName('img').draggable = false;
});
Now I'm learning backbone.js and trying to implement it in the Views, this is how it looks:
function noDrag () {
$(that.el).find('img').attr('draggable', false);
document.getElementsByTagName('img').draggable = false;
}
noDrag();
It doesn't work.
I know that the key to making this work is getting the part enter code heredocument.getElementsByTagName('img').draggable = false; to work. What's wrong with my code?
Here goes the full code:
window.dolnyPanelView = Backbone.View.extend({
tagName : 'div',
className : 'dolnyPanel-menu-view',
initialize : function() {
var that = this;
// tu wybierz template z templates/main.tpl
this.template = _.template($("#dolnyPanel-view").html());
return this;
},
events : {
},
render : function() {
var that = this;
$(this.el).html(this.template());
$(this.el).find('#panel-view').carousel({
interval: 3000
});
var BandCount;
$.post('api/getBandsCount.php', function(data) {
BandCount=data;
});
var items = getItems(BandCount);
$(this.el).find('.carousel-inner').html($(items));
$(this.el).find('.item').first().addClass('active');
function getItems(BandCount) {
// console.log(BandCount);
var allItems = '';
for (var i = 1; i <= BandCount; i++) {
var items = '';
for (var j = 0; j < 6; j++) {
if (i <= BandCount) {
items += getImageItem(i);
i++;
}
}
allItems += '<div class="item"><div class="row">' + items + '</div></div>';
}
return allItems;
}
function getImageItem(id) {
var item = '<div class="col-md-2 col-sm-3 col-xs-6 artist-col biography-artist-col"><a href="#x" bandId="'+id+'">';
var src = 'LEKSYKON';
$.post('api/getAwatar.php', {id: id}, function(data) {
src = src + data.path;
}, "json");
item += '<img src="' + src + '" alt="Image" class="img-responsive artist"></a></div>';
return item;
}
function noDrag () {
$(that.el).find('img').attr('draggable', false);
document.getElementsByTagName('img').draggable = false;
}
noDrag();
return this;
}
});
UPDATE: thank you for all the answers, it turned out that it's not working because the whole view doesn't work. The thread could be closed now not to mistake anybody.
document.getElementsByTagName returns a NodeList of DOM elements, so you'd need to apply the attribute change to every element rather than to the collection itself.
As you're using jQuery already, you don't need to use document.getElementsByTagName - you can create another jQuery selection.
In fact, that's exactly what you are doing in your first, working example - document.getElementsByTagName is not doing anything there.
You can use jQuery's prop method to reliably change a toggleable attribute for all elements in a selection.
$('img').prop('draggable', false);
See this question for an discussion of prop vs attr.
Try this
//this will get the first img element
var element = document.getElementsByTagName('img')[0];
//setting value of attribute
element.setAttribute("draggable", false);
I have a list of divs containing images/videos/galleries etc.
The structure is as follows:
<div class="item image">image content</div>
<div class="item video">video content</div>
<div class="item gallery">gallery content</div>
<div class="item image">image content</div>
<div class="item image">image content</div>
<div class="item video">video content</div>
As you can see, there can be more than one div with the same content type.
What I want to achieve is scan the list of divs with class=item and generate a button for each content type.
This is what I have so far, using jQuery EACH function
$(document).ready(function () {
$(".item").each(function () {
if ($(this).hasClass("image")) {
alert('image found');
};
if ($(this).hasClass("video")) {
alert('video found');
};
});
});
Problem is the alert get executed multiple times, for each div with the class equal to my condition. As I am planning to generate buttons for each content type this current code will add duplicate buttons as more than one div can have a class of video/image.
I have tried using "return false" inside the IF condition but that breaks my whole EACH function, stopping it at the first reference.
You can create a temporary variable that keeps track of which item types you have already traversed
(function() {
var types = {},
type_re = /\b(?:audio|video|quote|link|image|gallery|status|chat)\b/g;
$('.item').each(function() {
var m = this.className.match(type_re);
if (m !== null && !types.hasOwnProperty(m[0])) {
// code to add button
console.log('add button for type ' + m[0]);
types[m[0]] = true;
}
});
}());
Demo
Previous answers
You can create an array first that will contain all the types found in the document:
var types = [],
type_re = /audio|video|quote|link|image|gallery|status|chat/g;
$('.item').each(function() {
var m;
while ((m = type_re.exec(this.className)) !== null) {
if (!$.inArray(types, t[0])) {
types.push(t[0]);
}
}
});
// types is an array with all types found
Alternatively, iterate over all possible types and filter the items based on each type:
var $items = $('.item'),
types = ['audio', 'video', 'quote', 'link', 'image', 'gallery', 'status', 'chat'];
$.each(types, function(_, type) {
var $itemsOfType = $items.filter(function() {
return (' ' + this.className + ' ').indexOf(type) != -1;
});
if ($itemsOfType.length) {
}
});
Some really easy approach would be to add a status variable for each possible content type and check it:
$( document ).ready(function() {
var _image = true,
_video = true;
$( ".item" ).each(function() {
if ($(this).hasClass( "image" ) && _image) {
_image = false;
alert('image found');
};
if ($(this).hasClass( "video" ) && _video) {
_video = false;
alert('video found');
};
});
});
You can do this.
if($(".image").length>0)
{
alert("image Found")
//generate button
}
JSFIDDLE http://jsfiddle.net/rWrCA/
You can easily use an array for the types and another array for whether or not those exist. That looks something like:
$(document).ready(function () {
// a list of all types and a list of types that were found
var allTypes = ["image", "video", "gallery"];
var typesFound = [];
// loop over all items and add types to the list of found types
$(".item").each(function () {
for (var idx = 0; idx < allTypes.length; idx++) {
if ($(this).hasClass(allTypes[idx])) {
if (typesFound.indexOf(allTypes[idx]) < 0) {
typesFound.push(allTypes[idx]);
}
}
}
});
// as in the original code - prove this worked by displaying alerts!
for (var idx = 0; idx < typesFound.length; idx++) {
alert(typesFound[idx] + ' found');
}
});
I think that should do it!
Make an object in your jQuery ready function. When finished looping, write your buttons using the buttons object.
$(document).ready(function () {
var buttons = {};
$(".item").each(function () {
if ($(this).hasClass("image")) {
buttons.image = 1;
};
if ($(this).hasClass("video")) {
buttons.video = 1;
};
});
// write buttons
for (var type in buttons) {
$('<button/>', {
text: type,
id: 'btn_'+ type,
click: function () { alert('hi'); }
}).appendTo('body');
}
})
I'm using knockout.js. I have several editable grids in a form, and the names of the fields inside those grids matter, so using uniqueName isn't an option.
So what I'm doing is calling a function to rename the fields when the user clicks submit.
function renameFields(o, index) {
var el_name = $(o).attr('name');
var name_index = el_name.lastIndexOf('_') + 1;
$(o).attr('name', el_name.substring(0, name_index) + index);
}
function Submit() {
self.submit = function() {
// Use unique AND meaningful input/textarea [name]s in .grids.
window.index_arr = [];
$('.grid').each(function() {
$(this)
.children('.row')
.each(function(i){
window.index_arr.push(i);
$(this)
.find('input, textarea')
.each(function() {
renameFields($(this), window.index_arr[i]);
})
})
});
// Submit the form
return true;
}
}
renameFields works fine, but true is returned before it's called on every field.
Let Knockout do the dirty work of keeping the indices of your attributes up to date with something like this:
HTML:
<div id="grid" class="grid">
<div class="row" data-bind="foreach:rows">
<input data-bind="attr: { name: 'string_' + $index() }">
<textarea data-bind="attr: { name: 'string_' + $index() }">
</div>
</div>
View model:
var vm = {
rows: ko.observableArray()
};
ko.applyBindings(vm, document.getElementById("grid"));
Please give us more about how you are setting up the click handlers...
I'll take a SWAG at it... I think you are dealing with hoisting
Without a fiddle I can't test it, but try the following code.
var renameFields = function(o, index) {
var el_name = $(o).attr('name');
var name_index = el_name.lastIndexOf('_') + 1;
$(o).attr('name', el_name.substring(0, name_index) + index);
}
var Submit = function() {
self.submit = function() {
// Use unique AND meaningful input/textarea [name]s in .grids.
window.index_arr = [];
$('.grid').each(function() {
$(this)
.children('.row')
.each(function(i){
window.index_arr.push(i);
$(this)
.find('input, textarea')
.each(function() {
renameFields($(this), window.index_arr[i]);
})
})
});
// Submit the form
return true;
}
}
I am using event-delegation on n-number of rows, my older approach was binding each row with event the code looked something like this:
function getDiv (data) {
var div = $("<div class='theDiv'>");
div.click(function () {
console.log(data);
});
return div;
}
function getContainer() {
var i, container;
container = $("<div class='container'></div>");
for (i = 0 ; i < 1000 ; i++) {
container.append(getDiv(i));
}
return container;
}
$("body").append(getContainer());
Note: In this approach each row element (theDiv) is having access to their data.
Now the question is, I want to bind a single click on container and access the data, the event-delegation approach would look like this:
function getNewDiv (data) {
var div = $("<div class='theDiv'>");
return div;
}
function getNewContainer() {
var i, container;
container = $("<div class='container'></div>");
for (i = 0 ; i < 1000 ; i++) {
container.append(getNewDiv(i));
}
container.click (function (e) {
var targetElem = e.target;
console.dir(e);
if ($(targetElem).hasClass("theDiv")) {
console.log("row was clicked");
}
})
return container;
}
$("body").append(getNewContainer());
Now, how to access the data associated with each row?
As per my learning:
I can add the data to
data-*, but this would limit me to simple data type
$.data associated to element
Is there any other way todo this?
.on allows you to do event delegation:
container.on('click', '.theDiv', function () {
//`this` is `.theDiv`.
});
You can use $.data() inside getNewDiv() to store a reference index:
function getNewDiv (dataIndex) {
return $("<div class='theDiv'>").data('idx', dataIndex);
}
Then, use each element's data index variable to reference an object in your big array of data:
var mydata = [{ ... }, { ... }, { ... }];
container.on('click', '.theDiv', function () {
var data = mydata[$(this).data('idx')];
console.log("row was clicked");
});