Manipulate FontAwesome with Javascript - javascript

I'm working on a little project to learn more about Javascript so I can work on svg animations in the future.
Now I'm working on a button that will change his input when you click on it.
circle = document.getElementById('circle');
$remove = document.getElementById('remove');
remove.style.display = "block";
$undo = document.getElementById('undo');
undo.style.display = "none";
circle.onclick = function() {
remove.style.display = "none";
undo.style.display = "block";
}
.circle {
width: 200px; height: 200px;
background-color: #10ac84;
border-radius: 50%;
}
.circle svg { font-size: 1.5em; }
<div class="circle" id="circle">
<i class="fas fa-trash-alt" id="remove"></i>
<i class="fas fa-undo" id="undo"></i>
</div>
It's a little hard to put it in a snippet because I can't load the font awesome cdn.
-| What I'm trying to do |-
When you enter the page the circle will have an icon on a trashcan.
When the user clicks the circle the trashcan will disappear and the undo icon will show up.
This is for removing an item in my application and undo it when you want it back.
-| Question |-
Can I remove and add style to FontAwesome icons? Because it will transform into SVG's
I hope that I have explained it well and that someone can help me out.
Project: http://www.projectnova.nl/changeClick/

Make sure to target the right element whenever you use the SVG with JavaScript method. There won't be an <i> for you to select after the replacement.
Font Awesome uses mutation observers to know when it needs to change the SVG after you change the class.
document.getElementsByTagName("button")[0].addEventListener("click", evt => {
const icon = evt.currentTarget.querySelector("svg");
if (icon.classList.contains("fa-user")) {
icon.classList.remove("fa-user");
icon.classList.add("fa-github-square");
} else {
icon.classList.remove("fa-github-square");
icon.classList.add("fa-user");
}
});
<script defer src="https://use.fontawesome.com/releases/v5.0.12/js/all.js" integrity="sha384-Voup2lBiiyZYkRto2XWqbzxHXwzcm4A5RfdfG6466bu5LqjwwrjXCMBQBLMWh7qR" crossorigin="anonymous"></script>
<button><i class="fas fa-user"></i></button>

Be advised, this method only works when using Font Awesome with CSS. To use the JavaScript with SVG method, look at the other answer.
This would be easier to accomplish if you used a single i tag and dynamically changed the icon class.
For example:
<div class="circle">
<i class="fas fa-trash-alt" id="remove"></i>
</div>
Then you just flip between the fa-undo and fa-trash-alt classes.
// select the element
var removalIcon = document.querySelector("#remove");
// add event listener
removalIcon.addEventListener("click", function () {
this.className = "fas fa-undo";
});

Related

How to click on a button and click on it again to close all the content AND how to change image when you go into darkmode using HTML CSS or JS?

(I will link a code sandbox with all my code (at its latest) to be viewed if you want to)
https://codesandbox.io/s/distracted-hellman-pyej06?file=/index.html
I have two issues that I was hoping to address
Problem-1: I have a website called dirieahmed. ml (the same as the code sandbox) I have a night mode / light mode toggle, however when I click on night mode my logo (the image which is called hello.jpg and is above the face picture) will stay the same which makes sense but looks terrible because it looks like an imperfect white square. As a result, I wanted to change this. Therefore when I click on night mode I want the image to change into the night mode version of the logo I have made (it will be called hello-dark.jpg) Is there a way i can do that? I will link the appropriate code down below but if you would like to see the CSS you can view the sandbox
<div class="image1">
<img src="img/hello.jpg" alt="something" width="400" height="300">
</div>
<script async>
<--!This is my dark theme toggle-->
document.querySelector('.theme-toggle-button').addEventListener('click', () => {
document.body.classList.toggle('dark')
})
Problem-2: On my sandbox, you can view an About me, Education and Achievements and Other content elements, these elements are buttons when clicked they will drop down a content, if I click on about me then Education about me will open then close and allow Education to open. Pretty Normal and similarly for the button called "Other" but when i click on Other again to reset all the views and make it clean like when it was originally where no dropdowns exsisted that wont happen. Is there a way for me to make a javascript that will close all content from that div when clicked on it twice. (the code of CSS is in the sandbox but Html and JS will be here)
<div class="container">
<button id="one" class="click one" title="click me">About Me</button>
<div class="list-1 sec">
<h1 class="content-header-one content">Dummy text header</h1>
<p class="content-paragraph-one">Dummy text</p>
</div>
<button class="click two" title="click me">Education and Achivements</button>
<div class="list-2 sec">
<p class="content-paragraph2 content">Dummy text</p>
<ul class="content-list content">
<li>- Achivement #1</li>
<li>- Achivement #2</li>
<li>- Achivement #3</li>
</ul>
</div>
<button class="click three" title="click me" >Other</button>
<div class="list-3 sec">
<h1 class="content-header-two content">Dummy text header</h1>
<p class="content-paragraph3 content">Dummy text</p>
</div>
<script async>
// Instanciate List div's
let list1 = document.querySelector(".list-1");
let list2 = document.querySelector(".list-2");
let list3 = document.querySelector(".list-3");
// Reset's all views to the default without .newlist
const resetAllViews = () => {
list1.classList.remove("newlist");
list2.classList.remove("newlist");
list3.classList.remove("newlist");
};
// Checks to see what button is clicked and shows correct list based on input
document.addEventListener(
"click",
function (e) {
e = e || window.event;
var target = e.target;
if (target.classList.contains("one")) {
resetAllViews();
list1.classList.add("newlist");
}
if (target.classList.contains("two")) {
resetAllViews();
list2.classList.add("newlist");
}
if (target.classList.contains("three")) {
resetAllViews();
list3.classList.add("newlist");
}
}, false);
</script>
</div>
Again you can view the result I have in the sandbox (latest) and on the most recent website dirieahmed.ml for real life view
Sorry if I repeated myself a lot and any help would be appreciated though I prefer if you can show me the code as a runner snippet as I am fairly new so its a bit difficult for me to understand all vocabulary but again any help is appreciated
As for the 1st point, i think you have 2 possibilities:
Add the image as a background image with css, so you can easily toggle. Normally you show the "light" image, and if the body has the dark class then you can show the "dark" image.
The second idea is to add two <img> elements to the page and hide / show accordingly, something like:
.image-light {
display: block;
}
body.dark .image-light {
display: none;
}
.image-dark {
display: none;
}
body.dark .image-dark {
display: block;
}
As to the 2nd point i think you should to it like this:
you have a variable to hold the current open element
when you click a button you get the "value" vor that button
If the value if the same as the current open variable, then you reset the variable (maybe to null), otherwise you set the current open variable to the value of that button
Then you can have an update function. On this function you go through all the of "target" elements. If the element corresponds to the currently open variable, you open it, otherwise you close it.
Something like:
let currentlyOpen = null;
let buttons = document.querySelectorAll(".your-buttons");
let targets = document.querySelectorAll('.your-targets');
button.forEach((button) => {
button.addEventListener('click', (event) => {
const newTarget = event.target.getAttribute('your-identifier-attribute');
currentlyOpen = currentlyOpen === newTarget ? null : newTarget;
updateUI();
});
})
function updateUI() {
targets.forEach((target) => {
targetIdentifier = target.getAttribute('the-identifier-attribute');
targetIdentifier === currentlyOpen
? target.classList.add('your-class-for-open-list')
: target.classList.remove('your-class-for-open-list');
})
}
I have created an example for you:
let openContent = null;
const buttons = document.querySelectorAll('button');
const contents = document.querySelectorAll('.content');
buttons.forEach((button) => {
button.addEventListener('click', (event) => {
const newTargetId = event.target.getAttribute('target-id');
openContent = newTargetId === openContent ? null : newTargetId;
updateUI();
});
});
function updateUI() {
contents.forEach((content) => {
const contentId = content.getAttribute('id');
contentId === openContent
? content.classList.add('visible')
: content.classList.remove('visible');
});
}
.content {
display: none;
}
.content.visible {
display: block;
}
<p> click one of the buttons to show more content</p>
<button target-id="one">One</button>
<button target-id="two">Two</button>
<button target-id="three">three</button>
<p class="content" id="one">Content one</p>
<p class="content" id="two">Content two</p>
<p class="content" id="three">Content three</p>
Here is an example of doing this using two elements where one is hidden based on theme. You could make it a photo that you want to change. This way both images are loaded and you don't have to have the client wait for an image to load when themes change.
The way I accomplished this was mainly in css. The only javascript is to toggle the dark class on the page. The dark page has css variables which represent whether or not elements on the page have a display of none or block. On the actual elements which toggle, I feed those css variables into them. It is also possible to add animations or transitions to make it feel fancy, but hopefully this small demonstration satisfies your need for modifications to your own project.
const $ = str => document.querySelector(str);
const page = $(".page");
$(".theme-toggle").addEventListener("click", () => {
page.classList.toggle("dark");
});
body {
display: grid;
place-items: center;
}
.page {
--light-logo: block;
--dark-logo: none;
display: flex;
flex-direction: column;
text-align: center;
}
.page.dark {
--light-logo: none;
--dark-logo: block;
background-color: black;
color: white;
}
.logo-container {
font-size: 5rem;
}
.logo-container .dark {
display: var(--dark-logo);
}
.logo-container .light {
display: var(--light-logo);
}
<div class="page">
Hello World
<div class="logo-container">
<div class="light">๐ŸŒž</div>
<div class="dark">๐ŸŒ‘</div>
</div>
<button class="theme-toggle">๐ŸŒž / ๐ŸŒ‘</button>
</div>
Answer 1: It is possible to simple solve by CSS, but this is not good idea for the SEO optimization. Better and most simple solution is to use 2 images and add class "hidden", where you will set for this class {display:none} in css file. Next you must modify your script to set/remove "hidden" class by the selected light/dark mode.
<div class="image1">
<img src="img/hello.jpg" alt="something" width="400" height="300">
</div>
Another better solution is modify the DOM, replace img element when mode is changed, or change the src attribute, e.g.:
document.getElementById("myImageId").src="img/hello-dark.jpg";

How to add an if else statement in my code?

I am trying to an if/else statement added to this code where the toggle is already clicked to be able to click it again and change to another color
HTML
<a class="reme" href="#"><i class="far fa-circle"></i> Remember Me</a>
JQUERY
$(".reme").click(function(){
$(this).css("color", "#1ABC9C");
$(this).find('i').toggleClass('far fa-cirle fas fa-circle');
});
You can add a style to your CSS and then just toggleClass for the button itself - as demonstrated below.
Let me know if you wanted something else.
I have also added an if version below, but would advise against using it.
Demo
// Click event
$(".reme").click(function() {
// Toggle class on the button
$(this).toggleClass("green-text");
// Unchanged
$(this).find('i').toggleClass('far fa-cirle fas fa-circle');
});
.green-text {
color: #1ABC9C;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="reme">Toggle</button>
Alternative
You can use an if statement if you'd like, and check the inline .css value of color... but this is definitely not the best way of doing this. Toggling classes will be much better for this particular use case.
// Click event
$(".reme").click(function() {
// Check if the color is set inline to the rgb version of your color - as when changed this way it enters the hex as rgb
if ( $(this).css("color") == "rgb(26, 188, 156)") {
// Make it black if it is
$(this).css("color", "black");
} else {
// Set it as green if not
$(this).css("color", "#1ABC9C");
}
$(this).find('i').toggleClass('far fa-cirle fas fa-circle');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="reme">Toggle</button>

JS - mouseover and mouseout events are inconstant

HTML
<a class="level-item like-icon">
<div class="icon is-medium">
<i class="far fa-heart" onmouseover="change(true)" onmouseout="change(false)"></i>
</div>
</a>
JS
change = (state) => {
state
? event.currentTarget.setAttribute('data-prefix', 'fas')
: event.currentTarget.setAttribute('data-prefix', 'far');
};
Goal:
To change icon (class, or in this case, attribute) when someone hovers over the icon and revert it back when the user hovers out of it. The above code seems to work but there are a couple issues. 1) It fires way to many times when I hover over it and 2) Many times, it doesn't change the attribute back to "far" (state = false). I tried attaching those events to <a> instead of <li> but the issues persist.
p.s. NOT using JQUERY
Something like this one ?
Here i adding and removing a class hover, but ti also may be any attribute or something else
window.addEventListener('mousemove', e => {
let hovered = document.querySelector('.hover');
if (e.target === hovered) return;
if (hovered) {
console.log('mouse out from', hovered);
hovered.classList.remove('hover');
}
if (!e.target.classList.contains('icon'))
return;
e.target.classList.add('hover');
console.log('mouse over on', e.target)
})
.icon {
display: inline-block;
width: 50px;
height: 50px;
transition: 100ms;
border: solid;
text-align: center;
line-height: 50px;
}
.hover {
color: red;
border-radius: 30%;
transform: rotate(10deg)
}
<div class="icon">1</div>
<div class="icon">2</div>
<div class="icon">3</div>
<div class="icon">4</div>
<div class="icon">5</div>
<div class="icon">6</div>
<div class="icon">7</div>
There are 7 'onmouse...' events...
onmousedown,
onmouseenter,
onmouseleave,
onmousemove,
onmouseout,
onmouseover,
onmouseup
... so it is important to use the right one for the job.
In the example clicking and mouse movement within the Element doesn't apply - all we want is a function to be called once when the mouse enters and the element and once agian when the mouse leaves. Therefore...
<!-- HTML -->
<a class="level-item like-icon">
<div class="icon is-medium">
<i class="far fa-heart"
onmouseenter="change(this)"
onmouseleave="change(this)"></i>
</div>
</a>
So here it seems sensible to use the onmouseenter and onmouseleave attributes to call the change() function, and in this case to passes the HTML Element under the mouse as an argument via the this keywords.
Now the function can scrutinize the element and check if it has the desired and required 'data-prefix' attribute, and if so what that attribute is set to. We can then use this condition to set/reset the 'data-prefix' attribute's value...
/* JavaScript */
change = (elem) => {
// does the Element have a 'data-prefix' attribute
// and what is it set to?
let isSet = elem.hasAttribute("data-prefix") && (
"far" === elem.getAttribute("data-prefix")
);
elem.setAttribute("data-prefix", (isSet ? "fas" : "far"));
}
However, as has already been mentioned using the Element.addEventListener() method is more robust and flexible than relying on HTML attributes like onmouse....
This sounds like a duplicate of How do I simulate a mouseover in pure JavaScript that activates the CSS ":hover"?
It's not ideal to deal with mouseover in pure JS, but here is a working example (insipired by an answer to the post I linked).
var element = document.getElementById('hoverIcon');
element.addEventListener('mouseover', function() {
console.log('Mouse over, set Font Awesome class here');
});
element.addEventListener('mouseout', function() {
console.log('Mouse out, remove Font Awesome class here');
});
var event = new MouseEvent('mouseover', {
'view': window,
'bubbles': true,
'cancelable': true
});
<a class="level-item like-icon">
<div class="icon is-medium">
<i id="hoverIcon" class="far fa-heart">ICON</i>
</div>
</a>
Here is a second version of my initial answer, this time with multiple elements.
var elements = document.getElementsByClassName("hover-icon");
var i;
for (i = 0; i < elements.length; i++) {
element = elements[i];
element.addEventListener('mouseover', function(data) {
console.log('Mouse over, set Font Awesome of ID ' + data.originalTarget.id + " here");
});
element.addEventListener('mouseout', function(data) {
console.log('Mouse out, remove Font Awesome of ID ' + data.originalTarget.id + " here");
});
}
var event = new MouseEvent('mouseover', {
'view': window,
'bubbles': true,
'cancelable': true
});
<a class="level-item like-icon">
<div class="icon is-medium">
<i id="hoverIcon1" class="hover-icon far fa-heart">ICON1</i>
<i id="hoverIcon2" class="hover-icon far fa-heart">ICON2</i>
</div>
</a>

Adding icons to pseudo elements

I'm trying to add icons to my navigation (which dynamically changes) via JavaScript.
Here is my code (or JSFiddle):
var icon = document.querySelector('#icon');
icon.setAttribute('data-icon', 'ย†');
#icon:after {
content: attr(data-icon);
}
<div id="icon" data-icon="">
Icon
</div>
But it is not working, why? When tried to edit data-icon attribute manually, icon appears. Otherwise just unicode of icon.
HTML entities don't have any meaning in CSS. If you want to follow that path you need to decode it first:
var icon = document.querySelector('#icon');
var tmp = document.createElement("span");
tmp.innerHTML = 'ย†';
icon.setAttribute('data-icon', tmp.innerText);
#icon:after {
content: attr(data-icon);
}
<div id="icon" data-icon="">
Icon
</div>
... or, alternatively, just type character as-is (as long as your application is written in UTF-8):
var icon = document.querySelector('#icon');
icon.setAttribute('data-icon', 'โ€ ');
#icon:after {
content: attr(data-icon);
}
<div id="icon" data-icon="">
Icon
</div>
Last but not least, CSS also accepts escape sequences but you need to use the appropriate syntax. This approach, however, does not seem to work for your specific character:
var icon = document.querySelector('#icon');
// Brackets added for visibility, not part of the syntax
icon.setAttribute('data-icon', '(\u0086)(\u2020)');
#icon:after {
content: attr(data-icon);
}
<div id="icon" data-icon="">
Icon
</div>
I presume it's related to the fact that U+2020 'DAGGER' (that works fine) is a regular character but the one you're using U+0086 'START OF SELECTED AREA' (which doesn't show) is a control character. As you mention in a follow-up comment, you're using FontAwesome and such libraries provide custom fonts that map certain code points to display icons (though they normally use private areas to avoid conflicts).
That is obviously because setAttribute is escaping your special characters...and anyway you need to use CSS encoded icons and not an HTML entity. use this convertor:
ย† โ†’ \0086
I would suggest working with meaningful names instead of hard-coded writing the icon's code into an attribute.
For example:
var icon = document.querySelector('#icon');
icon.setAttribute('data-icon', 'icon-heart');
[data-icon=icon-heart]{
content:'\2764';
}
(better that all your icons elements will have a shared class which shared properties)
Encoding minefield
See this StackOverflow answer for more details.
Answer updated (see comments and edits).
In short, I recommend using the character instead of any method of encoding it. It will be vital in this case that your HTML files are correctly encoded and that they properly inform browsers what the encoding is.
var js = document.querySelectorAll( ".js" );
js[ 0 ].setAttribute( "data-icon", "\u0086" );
js[ 1 ].setAttribute( "data-icon", "โ€ " );
.css:after {
content: "\86";
color: red;
}
.css.char:after {
content: "โ€ ";
color: blue;
}
.data:after {
content: attr( data-icon );
}
.js:after {
content: attr( data-icon );
}
.red:after {
color: red;
}
.blue:after {
color: blue;
}
<div class="css">hard coded css content</div>
<div class="css char">hard coded css content char</div>
<br>
<div class="data red" data-icon="ย†">hard coded data-icon</div>
<div class="data blue" data-icon="โ€ ">hard coded data-icon char</div>
<br>
<div class="js red">data-icon by js</div>
<div class="js blue">data-icon by js char</div>

Display elements in order of click

How can I have elements .show in the order they're clicked and not the order they're appear in the HTML using jquery?
E.g.
Css code:
.sq{
display:none;
}
Html Code:
A
B
C
<span class="sq" id="01">a</span>
<span class="sq" id="02">b</span>
<span class="sq" id="03">c</span>
JavaScript code:
$("#1").click(function(){
$("#01").show();
});
$("#2").click(function(){
$("#02").show();
});
$("#3").click(function(){
$("#03").show();
});
Using this code if I click C,B,A the output will arrange "a b c"
What I would like is if I click C,B,A the output should arrange "c b a"
I've tried various CSS positioning rules to do this, but the best I can do is have them arrange in the same position as each other. I realize I could make a new class for each but would rather not do it that way in the interest of minimal code and I'm learning right now so it would be useful to know a better way around the issue.
Here's a fiddle: http://jsfiddle.net/xuxsuagg/4/
You can do something like
$(".myclass").one('click', function() {
$($(this).data('target')).appendTo('.output').show();
});
a {
text-decoration: none;
}
.sq {
display: none;
}
.output {}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
A
B
C
D
E
F
<p class="output">
<span class="sq" id="01">A</span>
<span class="sq" id="02">B</span>
<span class="sq" id="03">C</span>
<span class="sq" id="04">D</span>
<span class="sq" id="05">E</span>
<span class="sq" id="06">F</span>
</p>
Notes
Used a common event handler instead of using different handlers for each link
Before shown the element the target is moved to the last position of the parent
Used .one() to register the handler so that one element is shown only once
There is a very simple trick: use .append(). When you append a selected element that is already present in the DOM, you are actually moving it around. Also, I recommend that to optimize your code, you can:
Use a common class for the <a> elements
Assigned a HTML5 data- attribute, say data-target, to specify the ID of its intended target
Listen to click events triggered on the common class
An example of the proposed new markup:
A
B
<!-- and more -->
Here is the code (and the demo fiddle hereโ€”http://jsfiddle.net/teddyrised/xuxsuagg/9/)
$('.sq-click').click(function(e) {
// Prevent default action
e.preventDefault();
// Use .append() to move element
var $out = $('.output');
$out.find('#'+$(this).attr('data-target')).appendTo($out).show();
});
On a side note, if you do not want the users to rearrange the order after an anchor has been clicked, you will have to rely on the .one() method for listening to click events. Also, it will help that you style the disabled anchors appropriately so the users can see itโ€”see proof-of-concept demo: http://jsfiddle.net/teddyrised/xuxsuagg/26/
$('.sq-click').one('click', function(e) {
// Prevent default action
e.preventDefault();
// Use .append() to move element
var $out = $('.output');
$out.find('#'+$(this).attr('data-target')).appendTo($out).show();
// Add class to change appearance of disabled <a>
$(this).addClass('disabled');
});
And your CSS can look like this:
.disabled {
cursor: default;
opacity: 0.2;
}
You can simplify this code to:
var a = document.getElementsByTagName('a');
for (i = 0; i < a.length; i++) {
a[i].addEventListener('click', function() {
var span = document.createElement('span');
var text = document.createTextNode(this.innerHTML + " ");
span.appendChild(text);
document.getElementsByClassName('output')[0].appendChild(span);
})
}
a {
text-decoration: none;
}
.sq {
display: none;
}
A
B
C
D
E
F
<p class="output">
</p>
Bind the click event to the class that they share and not their own unique id.
In the function scope of clickClosure 'this' is referring to the current element.
$(".sq").click(function clickClosure(){
$(this).show();
});
Using styles to achieve this might range from painful to very hard, depending on the exact way you want them displayed. I'd suggest instead to re-order them in DOM. That might look something like this:
<a id="link-1">...</a>
...
<div style="display: none" id="hidden-items">
<span id="item-1">...</span>
</div>
<div id="visible-items"></div>
&
$('#link-1').click(function () {
$('#visible-items').append($('#item-1'));
});
As other respondents suggested, you could also optimize your code in various ways, but that's outside the scope of the question.
Try this: You can put empty <p class="output"> and remove display:none; from CSS .sq{... In jQuery, create a <span> on the basis of clicked link and append it to <p class="output">
HTML:
A
B
C
D
E
F
<p class="output">
</p>
CSS:
.sq{
/*display:none;*/
color: green;
margin-right: 10px;
}
jQuery
$("a[href='#']").click(function(e){
e.preventDefault();
var text = '<span class="sq" id="0'+$(this).prop('id')+'">'
+$(this).text()+'</span>';
$(text).appendTo('p.output');
});
DEMO

Categories

Resources