How to toggle a form through button? - javascript

I would like to show a form through button click in JS vanilla but nothing work. Here is my code
/* ======= Model ======= */
var model = {
currentCat: null,
cats: [
{
clickCount : 0,
name : 'Tabby',
imgSrc : 'img/434164568_fea0ad4013_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/bigtallguy/434164568'
},
{
clickCount : 0,
name : 'Tiger',
imgSrc : 'img/4154543904_6e2428c421_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/xshamx/4154543904'
},
{
clickCount : 0,
name : 'Scaredy',
imgSrc : 'img/22252709_010df3379e_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/kpjas/22252709'
},
{
clickCount : 0,
name : 'Shadow',
imgSrc : 'img/1413379559_412a540d29_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/malfet/1413379559'
},
{
clickCount : 0,
name : 'Sleepy',
imgSrc : 'img/9648464288_2516b35537_z.jpg',
imgAttribution : 'https://www.flickr.com/photos/onesharp/9648464288'
}
]
};
/* ======= Octopus ======= */
var octopus = {
init: function() {
// set our current cat to the first one in the list
model.currentCat = model.cats[0];
// tell our views to initialize
catListView.init();
catView.init();
adminView.init();
},
getCurrentCat: function() {
return model.currentCat;
},
getCats: function() {
return model.cats;
},
// set the currently-selected cat to the object passed in
setCurrentCat: function(cat) {
model.currentCat = cat;
},
// increments the counter for the currently-selected cat
incrementCounter: function() {
model.currentCat.clickCount++;
catView.render();
}
};
/* ======= View ======= */
var catView = {
init: function() {
// store pointers to our DOM elements for easy access later
this.catElem = document.getElementById('cat');
this.catNameElem = document.getElementById('cat-name');
this.catImageElem = document.getElementById('cat-img');
this.countElem = document.getElementById('cat-count');
// on click, increment the current cat's counter
this.catImageElem.addEventListener('click', function(){
octopus.incrementCounter();
});
// render this view (update the DOM elements with the right values)
this.render();
},
render: function() {
// update the DOM elements with values from the current cat
var currentCat = octopus.getCurrentCat();
this.countElem.textContent = currentCat.clickCount;
this.catNameElem.textContent = currentCat.name;
this.catImageElem.src = currentCat.imgSrc;
}
};
var catListView = {
init: function() {
// store the DOM element for easy access later
this.catListElem = document.getElementById('cat-list');
// render this view (update the DOM elements with the right values)
this.render();
},
render: function() {
var cat, elem, i;
// get the cats we'll be rendering from the octopus
var cats = octopus.getCats();
// empty the cat list
this.catListElem.innerHTML = '';
// loop over the cats
for (i = 0; i < cats.length; i++) {
// this is the cat we're currently looping over
cat = cats[i];
// make a new cat list item and set its text
elem = document.createElement('li');
elem.textContent = cat.name;
// on click, setCurrentCat and render the catView
// (this uses our closure-in-a-loop trick to connect the value
// of the cat variable to the click event function)
elem.addEventListener('click', (function(catCopy) {
return function() {
octopus.setCurrentCat(catCopy);
catView.render();
};
})(cat));
// finally, add the element to the list
this.catListElem.appendChild(elem);
}
}
};
var adminView = {
init: function() {
this.formElement = document.getElementById('admin-form');
this.buttonAdmin = document.getElementById('admin-button');
console.log(this);
this.render();
},
render: function() {
this.formElement.style.display = 'none';
console.log(this);
this.buttonAdmin.addEventListener('click', (function(form){
return form.style.display = 'block';
})(this.formElement));
console.log(this);
}
};
// make it go!
octopus.init();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cat Clicker</title>
</head>
<body>
<ul id="cat-list"></ul>
<div id="cat">
<h2 id="cat-name"></h2>
<div id="cat-count"></div>
<img id="cat-img" src="" alt="cute cat">
</div>
<div>
<button type="button" id="admin-button">Admin</button>
</div>
<div id="admin-form">
<p>Name</p>
<input type="text" name="new-cat-name" value="">
<p>Img URL</p>
<input type="text" name="new-img-url" value="">
<p># Count</p>
<input type="text" name="new-count-number" value="">
<br>
<button type="button" name="save">Save</button>
<button type="button" name="cancel">Cancel</button>
</div>
<script src="js/app.js"></script>
<script>
if(!document.getElementById('admin-button').active){
document.getElementById('admin-form').style.display ='none';
}
</script>
</body>
</html>
I don't understand why the form (#admin-form) is shown when the page is loaded, it should be shown only when the button (#admin-button) is clicked.
Update: I added the script to hide the form in HTML file.

Here's your problem:
this.buttonAdmin.addEventListener('click', (function(form){
return form.style.display = 'block';
})(this.formElement));
You wrap the handler function in parens and call it immediately. Try:
this.buttonAdmin.addEventListener('click', function(){
this.formElement.style.display = 'block';
}.bind(this));
Or, if you don't want to use bind for whatever reason:
var form = this.formElement;
this.buttonAdmin.addEventListener('click', function(){
form.style.display = 'block';
});

Related

How to switch images using JS?

I have a simple code that switches text when an image is clicked:
js:
$(document).ready(function() {
$('.srb').on('click', function() {
var r = $('.localization').each(function() {
var el = $(this);
var key = (el.attr('caption'));
el.text(srb[key]);
});
});
$('.eng').on('click', function() {
var r = $('.localization').each(function() {
var el = $(this);
var key = (el.attr('caption'));
el.text(eng[key]);
});
});
var srb = {
welcome: 'Добро дошли на наш сајт!'
};
var eng = {
welcome: 'Welcome to our site!'
};
});
HTML:
<span class='localization' caption='welcome'>Welcome to our site!</span>
<img src="img/Serbia.png" value='srb' class="srb" id="flag"/>
<img src="img/United-Kingdom.png" class='eng' value='eng'/>
Is it possible to switch images when language is switched (for example, when English language is set, GB flag disappears)?
Edit html like that
<img src="img/Serbia.png" value='srb' class="image_flag srb" id="flag"/>
<img src="img/United-Kingdom.png" class="image_flag eng" value="eng"/>
add class hidden element
.d-none{
display: none !important;
}
<script>
function activeImageFlag(flagActive){
document.querySelectorAll(".image_flag").forEach(function(flagImage){
flagImage.classList.add('d-none')
});
document.querySelector(".image_flag." + flagActive).classList.remove('d-none')
}
</script>

How to edit and delete individual items from CRUD app

I'm trying to write a CRUD app. I'm having trouble figuring out how to edit and delete individual items. For each item created, I'm making two <a> tags inside of a <span> tag. One for edit and one for delete. But I can't seem to figure out how to make them do what they need to do. At this point they don't do anything because I can't figure out how to access the values correctly.
Note - I'm just beginning to learn jQuery so, any pro tips on that are appreciated.
Here's the html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div class="container">
<form class='form'>
<input id="input" type="text" placeholder="Type here..">
</form>
<h3>Notes</h3>
<ul></ul>
<button id='clear'>Clear All</button>
</div>
<script src="app.js"></script>
</body>
</html>
And the javascript:
const app = {};
app.counter = (function(){
var i = -1;
return function(){
i += 1;
return i;
}
})()
app.create = function(element){
return document.createElement(element);
}
app.select = function(element){
return document.querySelector(element);
}
app.makeList = function(text) {
var i = app.counter();
var li = app.create('li');
var div = app.create('span');
var edit = app.create('a');
var del = app.create('a');
li.textContent = text;
edit.textContent = ' Edit';
edit.href = '#'
del.textContent = ' Delete';
del.href = '#'
div.appendChild(edit);
div.appendChild(del);
li.appendChild(div);
ul.insertBefore(li, ul.childNodes[0])
li.id = 'item' + i;
del.id = 'delete' + i;
edit.id = 'edit' + i;
}
// constants & variables
const ul = app.select('ul')
const input = app.select('input')
var notes;
$(document).ready(function(){
if (localStorage.getItem('notes')) {
notes = JSON.parse(localStorage.getItem('notes'));
} else {
notes = [];
}
localStorage.setItem('notes', JSON.stringify(notes));
// build list items and display them on the page
JSON.parse(localStorage.getItem('notes')).forEach(function(item){
app.makeList(item);
});
// when form is submitted
$('.form').submit(function(e){
e.preventDefault();
if (input.value.length > 0){
notes.push(input.value);
localStorage.setItem('notes', JSON.stringify(notes));
app.makeList(input.value);
input.value = "";
}
})
// clear items on page and from local storage
$('#clear').click(function(){
if (window.confirm('This will clear all items.\nAre you sure you want to do this?')){
localStorage.clear();
while (ul.firstChild) {
ul.removeChild(ul.firstChild)
}
}
});
$('ul').on('click', 'li', function(){
console.log(this.textContent) // logs whatever is typed + Edit Delete
})
});
Do something like this.
$("ul").on("click", "li", function(e) {
console.log(this.textContent); // logs whatever is typed + Edit Delete
if(e.target.id === "edit") {
//edit
}
if(e.target.id==="delete") {
//delete
}
});
You are trying to access elements before they are ready that is why you are not able to see anything.
Declare them on global level but assign them value after the document is ready.
var ul;
var input;
var notes;
$(document).ready(function () {
ul = app.select('ul')
input = app.select('input')
...Rest of your code
});
For the Edit and Delete Functionality
As you are appedning IDs in edit and delete button you need to parse that as well
$('ul').on('click', 'li', function (e) {
if (e.target.id.includes('edit')) {
console.log(` item ${e.target.id.split('edit')[1]} needs to be edited.`)
}
if (e.target.id.includes('delete')) {
//delete
}
})

Dynamically change button label inside ng-repeat

Consider a simple ng-repeat which creates a directive in each iteration. Each directive contains a button that triggers a function. These buttons should have the label set to "Show", and should change to "Hide" when the button is clicked. When I click on a button I want to check if there are other buttons set to "hide": if yes, they should revert to "show". Basically my goal is to only have one button with the label set to "Hide", others should always be "Show". How can I do that?
<div ng-repeat="campaign in $root.transactions">
<my-directive campaign='campaign' index='$index></my-directive>
</div>
myDirective.html:
<div>
..some stuff...
<button ng-click="toggleDetail()">{{labelButton}}</button>
</div>
js:
$scope.labelButton = 'Show';
$scope.detailOpened = false;
$scope.labelButton = 'Show';
$scope.$root.selectedIndex = -1;
$scope.toggleDetail = function($event, index){
...do stuff...
$scope.detailOpened = !$scope.detailOpened;
$scope.$root.selectedIndex = index;
$(element).toggleClass('selectedActivity');
if($scope.detailOpened === false) {
$scope.labelButton = 'Show';
}else {
$scope.labelButton = 'Hide';
}
};
With ng-repeat, you'll need an array in $scope. Using directive will do, but may not be necessary.
I have made a jsfiddle here: http://jsfiddle.net/goodman/z9kg0md0/15/embedded/result/
I wonder if this is what you want. Codes are here:
angular.module("MyApp",[])
.controller( 'myController', [ '$scope', function( $scope ){
$scope.buttons = [
{ detailOpened: false, label: 'Show1'},
{ detailOpened: false, label: 'Show2'},
{ detailOpened: false, label: 'Show3'},
{ detailOpened: false, label: 'Show4'},
{ detailOpened: false, label: 'Show5'}
];
$scope.toggleDetail = function(index){
$scope.buttons[index].detailOpened = !$scope.buttons[index].detailOpened;
if(!$scope.buttons[index].detailOpened) {
$scope.buttons[index].label = 'Show';
}else {
$scope.buttons[index].label = 'Hide';
}
if( $scope.buttons[index].detailOpened ){
for( var i = 0; i < $scope.buttons.length ; i++ ){
if( i != index && $scope.buttons[i].detailOpened) {
$scope.buttons[i].detailOpened = false;
$scope.buttons[i].label = 'Show';
}
}
}
};
}]);
and html:
<div ng-app="MyApp" ng-controller="myController">
<div ng-repeat="button1 in buttons">
<button ng-click="toggleDetail($index)">
{{button1.label}}
</button>
</div>
</div>

CSSTransitionGroup (React) complains about not finding key

I am trying to use CSSTransitionGroup to animate Sentences in a SentenceList. When the "next" button is pressed I want the next Sentence to animate in and the 1st in the list to fadeout. However I get this error message:
Each child in an array should have a unique "key" prop. Check the
render method of Sentence.
I don't understand why that is since when I push Sentence into my List I am passing it a {sentence.id} as a "key" prop. Shouldn't React know that each sentence key is defined as such when rendering it?
I've tried defining the key again in the Sentence render method but to no avail. Are my State changes making React lose track of the current Sentence key?
Thanks for your help!
SentenceList:
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var SentenceList = React.createClass({
getInitialState: function() {
return {
sentences: this.props.sentences
}
},
//receives sentence and new blip from Sentence
addBlip: function(sentence, value) {
//see where in the loaded sentences we are
var i = this.state.sentences.indexOf(sentence),
sentences = this.state.sentences,
// callback within a callback (post), the context changes inside the callback so we need to set this to self
self = this;
$.post(
'/sentences/' + sentence.id + '/blips',
{blip: {body: value}},
//set sentence we blipped into as answered
//reset state to reload sentences state after post
function(response) {
sentences[i].answered = true;
// sentences[i].statistics = response.statistics;
// put dummy content first then work it out in the backend to receive the format you want to receive (better to work from front to back)
sentences[i].statistics = [
{word: "butts", frequency: "95%"},
{word: "dogs", frequency: "2%"},
{word: "vegetables", frequency: "1%"},
{word: "sun", frequency: "2%"}
];
self.setState({sentences: sentences});
});
},
//take in a sentence (sent from Sentence) and find current position in loaded sentences and set it to dismissed, then reload list
dismissSentence: function(sentence) {
var i = this.state.sentences.indexOf(sentence),
sentences = this.state.sentences;
sentences[i].dismissed = true;
this.setState({sentences: sentences});
},
//list undismissed sentences and take out the first 3 for display
topThreeRemainingSentences: function() {
var unanswered = _.where(this.state.sentences, {dismissed: false});
return unanswered.slice(0, 3);
},
render: function() {
var remaining = this.topThreeRemainingSentences(),
sentences = [],
index = 0;
//loop through sentences until we have 3 remaining sentences loaded
while (index <= (remaining.length - 1)) {
var sentence = remaining[index];
sentences.push(
<Sentence key={sentence.id}
isActive={index == 0}
isNext={index == 1}
isNnext={index == 2}
onDismiss={this.dismissSentence}
onSubmitBlip={this.addBlip}
details={sentence} />
)
index = index + 1;
}
return (
<ReactCSSTransitionGroup transitionName="animate">
<div>{sentences}</div>
</ReactCSSTransitionGroup>
)
}
});
Sentence:
var Sentence = React.createClass({
getDefaultProps: function() {
return {
onSubmitBlip: function() { console.log(arguments) }
}
},
//pass sentence and new blip to submit function
addBlip: function(e) {
e.preventDefault();
var blipBody = this.refs.newBlip.getDOMNode().value
this.props.onSubmitBlip(this.props.details, blipBody);
},
//send sentence to List to set it to dismissed
dismissSentence: function(e) {
e.preventDefault();
this.props.onDismiss(this.props.details);
},
render: function() {
var phrase = this.props.details.body,
phrase_display = phrase.split("*"),
before = phrase_display[0],
after = phrase_display[1],
positionClass,
stats;
if (this.props.isActive) {
positionClass = "active-sentence"
} else if (this.props.isNext) {
positionClass = "next-sentence"
} else if (this.props.isNnext) {
positionClass = "nnext-sentence"
}
//find stats for sentence if answered from json and push them into array ["word", x%]
if (this.props.details.answered) {
var words = [];
this.props.details.statistics.forEach(function(statistic) {
words.push(<li className="stats-list"><span className="stats-list-word">{statistic.word} </span>
<span className="stats-list-percent">{statistic.frequency} </span> </li>)
})
stats = <div><span className="stats-list-header">others said:</span> {words}</div>
}
if (this.props.isActive) {
nextButton = <div className="next-button" onClick={this.dismissSentence}>V</div>
}
if (this.props.isNext) {
nextButton = <div></div>
}
if (this.props.isNnext) {
nextButton = <div></div>
}
return (
<div className={"blipForm " + positionClass}>
{before}
<form onSubmit={this.addBlip}>
<input type="text"
ref="newBlip" />
</form>
{after}
{nextButton}
<br/>
<ul>{stats}</ul>
</div>
)
}
});
The <li> elements created in the Sentence component's render method need key attributes:
this.props.details.statistics.forEach(function(statistic) {
words.push(<li className="stats-list" key={statistic.id}>...</li>);
});

Firebase events not triggered properly when joining tables with Firebase-util

I'm using Firebase-util's intersection function to find all the comments for a given link. This seems to work fine the first time I call the join, but it doesn't seem to properly notify my value callback when I remove the contents of the database and replace them again. Shouldn't the references keep working as long as the resource path remains the same?
Run this example. When you click the button, it erases and recreates the data. As you can see, the comment list is not repopulated after the data gets recreated.
<link rel="import" href="https://www.polymer-project.org/components/polymer/polymer.html">
<script src="http://cdn.firebase.com/v0/firebase.js"></script>
<script src="https://cdn.firebase.com/libs/firebase-util/0.1.0/firebase-util.min.js"></script>
<polymer-element name="my-element">
<template>
<h1>Test of Firebase-util.intersection</h1>
<div>
<button on-click={{initializeFirebase}}>Reset data</button>
</div>
<ul>
<template repeat="{{rootComment in comments}}">
<li>{{rootComment.comment.content}}
<ul>
<template repeat="{{subComment in rootComment.children}}">
<li>{{subComment.comment.content}}
<ul>
<template repeat="{{subSubComment in subComment.children}}">
<li>{{subSubComment.comment.content}}</li>
</template>
</ul>
</li>
</template>
</ul>
</li>
</template>
</ul>
</template>
<script>
Polymer('my-element', {
ready: function() {
var sanitizeUrl = function(url) {
return encodeURIComponent(url).replace(/\./g, '%ZZ');
};
var baseUrl = "https://nested-comments-test.firebaseio.com";
var linkUrl = baseUrl +
'/links/' +
sanitizeUrl(document.URL) +
'/comments';
var commentsUrl = baseUrl + '/comments';
var root = new Firebase(baseUrl);
this.initializeFirebase = function() {
function addLink(url, callback) {
var key = sanitizeUrl(url),
newLink = {
url: url,
createdAt: Firebase.ServerValue.TIMESTAMP
};
root.child('/links/' + key).update(newLink);
callback(key);
}
function addComment(attributes, callback) {
return root.child('/comments').push(attributes, callback);
}
function onCommentAdded(childSnapshot) {
var newCommentId = childSnapshot.name(),
attributes = {},
link = childSnapshot.val().link,
url = '/links/' + link + '/comments';
attributes[newCommentId] = true;
root.child(url).update(attributes);
}
root.remove(function() {
root.child('/comments').on('child_added', onCommentAdded);
addLink(document.URL, function(link) {
var attributes = {
link: link,
content: "This is the first comment."
},
firstCommentId, secondCommentId;
firstCommentId = addComment(attributes).name();
attributes = {
link: link,
content: "This is a reply to the first.",
replyToCommentId: firstCommentId
};
secondCommentId = addComment(attributes).name();
attributes = {
link: link,
content: "This is a reply to the second.",
replyToCommentId: secondCommentId
};
addComment(attributes);
attributes = {
link: link,
content: "This is another reply to the first.",
replyToCommentId: firstCommentId
};
addComment(attributes);
});
});
};
this.initializeFirebase();
var findChildrenForComment = function(snapshot, parentCommentId) {
var returnVal = [];
snapshot.forEach(function(snap) {
var comment = snap.val(),
commentId = snap.name();
if (comment.replyToCommentId === parentCommentId) {
var children = findChildrenForComment(snapshot, commentId);
var obj = {
commentId: commentId,
comment: comment,
parentId: parentCommentId
};
if (children.length) {
obj.children = children;
}
returnVal.push(obj);
}
});
return returnVal;
};
this.ref = Firebase.util.intersection(
new Firebase(linkUrl),
new Firebase(commentsUrl)
);
this.comments = {};
var that = this;
this.ref.on('value', function(snapshot) {
that.comments = findChildrenForComment(snapshot);
});
}
});
</script>
</polymer-element>
<my-element></my-element>
Apparently deleting a path entirely causes all callbacks on it to be canceled. The workaround for this behavior is to remove children one at a time rather than deleting their parent path.

Categories

Resources