I am using sveltekit, typscript, and tailwind for this. Ok, I have a website that I am making and I have buttons that have no background and are formated like this:
When opened
- Label
stuff
when closed
+ Label
It worked and all but when I added a new div to be right next to those buttons the buttons stopped working completely. It would not even show the pointer hand.
Code for buttons:
<script lang="ts">
let expanded: boolean = true;
export let item: string = '';
export let value: string[] = [];
import Page from '../Components/page.svelte';
let pagen: string = 'About';
</script>
<Page bind:page={pagen} run="" />
<div class="dropdown flex flex-col mt-5">
<button
class="dropdown-toggle"
on:click={() => {
expanded = !expanded;
}}
>
<span class="dropdown-label text-gray-400 ml-5 flex text-lg"
>{expanded ? `- ${item}` : `+ ${item}`}</span
>
</button>
<div class="dropdown-content flex" style:display={expanded ? 'block' : 'none'}>
{#each value as val}
<div class="dropdown-item text-gray-400 ml-10">
<p class="inline-block text-gray-400">#</p>
<button
on:click={() => {
pagen = `{val}`;
}}>{val}</button
>
</div>
{/each}
</div>
</div>
The pagen that is binded is for the div that goes right next to it. (this is not important I think...)
Code for the page:
<script lang="ts">
export let page: string = 'About';
export let run: string = '';
</script>
{#if run == 'true'}
<div class="w-screen h-screen -z-10">
<div class="page ml-80 bg-gray-800 h-screen">
{#if page == 'About'}
<div class="flex flex-col items-center justify-center">
<h1 class="text-gray-400 text-3xl font-bold tracking-wider mt-
10">Some Title</h1>
<p class="text-gray-400 text-2xl font-bold tracking-wider">Some
Label</p>
</div>
{/if}
</div>
</div>
{/if}
These are both components in svelte and are imported in index.svelte. Buttons on top and page on bottom.
Helpful Images:
The website is being styled to look like discord.
Any help is greatly appreciated!
There are a few things I'm noticing that may help you get this working:
Nothing may be showing because values may be empty. When I added values = [ "a", "b", "c" ] I saw them no problem (see repl).
You're not setting run, so Page will never render. Also, using run as a string is weird since it appears it is a boolean?
If you're using Tailwind, instead use conditional classes:
<div class="dropdown-content flex" style:display={expanded ? 'block' : 'none'}>
<!-- should be: -->
<div class="dropdown-content flex" style:block={expanded} style:hidden={!expanded}>
Stylistic recommendation:
<span class="dropdown-label text-gray-400 ml-5 flex text-lg">
{expanded ? `- ${item}` : `+ ${item}`}
</span>
<!-- simpler: -->
<span class="dropdown-label text-gray-400 ml-5 flex text-lg">
{expanded ? "-" : "+"} {item}
</span>
Here is a repl with what I believe is working code.
Related
The problem:
class="hover:bg-blue-400 hover:-translate-y-2 hover:-translate-x-2 hover:scale-110 hover:shadow-2xl hover:shadow-blue-400 hover:text-white"
here you see, there is the same prefix repetition.
hover:foo hover:bar hover:hello hover:world hover:something hover:another
I want to know if is there a way to not write multiple times the hover: prefix?
The idea:
is do something like:
hover:(class class class class class)
with brackets or something like that, so all the classes inside the () will be like one class and automatically added to the hover:
I think this idea there is in tailwind but I don't know the syntax for that.
if is possible this solution needs to work also with all the other prefixes
simple example demo:
// not important, only for deleting the console.warn()
console.clear();
<script src="https://cdn.tailwindcss.com"></script>
<body class="flex h-screen">
<button class="m-auto p-4 rounded-md bg-blue-200 transition hover:bg-blue-400 hover:-translate-y-2 hover:-translate-x-2 hover:scale-110 hover:shadow-2xl hover:shadow-blue-400 hover:text-white">
hello world
</button>
</body>
I saw all the docs, that is not talking about this concept: https://tailwindcss.com/docs/hover-focus-and-other-states#hover-focus-and-active
if there is someone experienced in this thing, it will be helpful!
like he said #diego in the comment, this is technically not possible with tailwind only.
tailwind framework alternative
maybe you use a tailwind framework like windiCSS https://windicss.org/features/variant-groups.html
that have this functionality:
<div class="hover:(bg-gray-400 font-medium) bg-white font-light"/>
javascript vanilla (simple script)
want tailwind only?
so I think that maybe we can create a simple JS script to solve this problem.
twHover();
function twHover() {
// get only the elements that have the hover attribute
let hoverEls = document.querySelectorAll("[data-hover]");
// loop through the elements that have the hover attribute
hoverEls.forEach((el) => {
// we get the string inside the attribute
// and then make it into a array
let twHoverClasses = `${el.dataset.hover}`.split(" ");
// loop through the classes inside the element's attributes
twHoverClasses.forEach((className) => {
// add the class for you `hover:className`
el.classList.add(`hover:${className}`);
});
});
}
<script src="https://cdn.tailwindcss.com"></script>
<body class="flex h-screen">
<!-- original -->
<button class="m-auto p-4 rounded-md bg-blue-200 transition hover:bg-blue-400 hover:-translate-y-2 hover:-translate-x-2 hover:scale-110 hover:shadow-2xl hover:shadow-blue-40 hover:text-white">original</button>
<!-- with script -->
<button data-hover="bg-blue-400 -translate-y-2 -translate-x-2 scale-110 shadow-2xl shadow-blue-40 text-white" class="m-auto p-4 rounded-md bg-blue-200 transition">with script</button>
</body>
want more from the JS script?
also :focus, :lg, :sm, and so on.
use this:
// this can be any preudo class that tailwind can have
twPseudo("focus");
// if there is nothing as parameter, we use hover
twPseudo();
function twPseudo(pseudo = "hover") {
// get only the elements that have the hover attribute
let hoverEls = document.querySelectorAll(`[data-${pseudo}]`);
// loop through the elements that have the hover attribute
hoverEls.forEach((el) => {
// we get the string inside the attribute
// and then make it into a array
let twHoverClasses = `${el.dataset[pseudo]}`.split(" ");
// loop through the classes inside the element's attributes
twHoverClasses.forEach((className) => {
// add the class for you `hover:className`
el.classList.add(`${pseudo}:${className}`);
});
});
}
<script src="https://cdn.tailwindcss.com"></script>
<body class="grid grid-cols-2 place-items-center h-screen">
<!-- original -->
<div>
<h2 class="text-3xl font-bold text-blue-500 mb-4">original</h2>
<!-- hover -->
<button class="m-auto p-4 rounded-md bg-blue-200 transition hover:bg-blue-400 hover:-translate-y-2 hover:-translate-x-2 hover:scale-110 hover:shadow-2xl hover:shadow-blue-40 hover:text-white">hover</button>
<!-- focus -->
<button class="m-auto p-4 rounded-md bg-blue-200 transition focus:bg-blue-400 focus:-translate-y-2 focus:-translate-x-2 focus:scale-110 focus:shadow-2xl focus:shadow-blue-40 focus:text-white">focus</button>
</div>
<!-- with script -->
<div>
<h2 class="text-3xl font-bold text-blue-500 mb-4">with script</h2>
<!-- hover -->
<button data-hover="bg-blue-400 -translate-y-2 -translate-x-2 scale-110 shadow-2xl shadow-blue-40 text-white" class="m-auto p-4 rounded-md bg-blue-200 transition">hover</button>
<!-- focus -->
<button data-focus="bg-blue-400 -translate-y-2 -translate-x-2 scale-110 shadow-2xl shadow-blue-40 text-white" class="m-auto p-4 rounded-md bg-blue-200 transition">focus</button>
</div>
</body>
also make sure to put the script code at the end of the page or inside a DomContentLoaded event
advantages:
less repetitive characters to type
more than 25 chars saved (only in your example)
multiple line attribute
As you can see you can write your classes in one line,
and the hover logic in another line. making it easy to debug.
works out of the box.
just copy and paste, and call the function.
with the correct parameter (focus, sm, lg, xl, 2xl) or without any parameter (will be hover)
// just call it at the end of the page
twPseudo();
You can just create a new class in a <style> block in your page or template. And then use #apply to use the needed tailwind classes. Like:
<style>
.mybutton {
#apply m-auto p-4 rounded-md bg-blue-200 transition
}
.mybutton:hover {
#apply bg-blue-400 -translate-y-2 -translate-x-2 scale-110 shadow-2xl shadow-blue-400 text-white
}
</style>
Now, if you set the mybutton class on the button, the hover will also work.
You can also add these classes to the main css file of your project. This is not the preferred way of tailwind, though. See Tailwind documentation.
For people using react who stumble upon the question, there is a better approach for the following:
const pseudoJoin = (selector, str) => {
return selector+":"+str.split(" ").join(" "+selector+":")
}
Now you can call it anywhere like:
<div className=`${pseudoJoin('hover','classes you want on hover')} some more classes`>Hello World!</div>
Or when you are using classnames framework:
<div className={ classnames(
pseudoJoin('hover', 'classes you want on hover'),
"Other classes here"
)}>Hello World!</div>
Hello I need a small help I want to hide button background and I am unable to apply flex which wraps the items contained in a container in which it is applied into single line
My html code:
import {
GlobeIcon,
HomeIcon
}from '#heroicons/react/outline'
function Sidebar(){
return (
<div>
<button className='flex items-center space-x-2 hover:text-white'>
<HomeIcon className = 'h-5 w- 5'/>
<p1>Home</p1>
</button>
<button className='flex items-center space-x-2 hover:text-white'>
<GlobeIcon className = 'h-5 w- 5'/>
<p1>Explore</p1>
</button>
</div>
)
}
export default Sidebar;
I want my page to look like
But my actual page is shwing like
Anybody could help me with it
I am using vue3 with vitejs in one of my projects and I have stumbled upon an issue. I have the following codes.
<div>
<p class="line-clamp-4">Some really long text here...</p>
<a id="read-more" href="readmore.html">Read more</a>
</div>
I am using tailwindcss here and I was able to clamp 4 lines. I have a list of these texts and not every one of them will have line clamp. So, I only want to show the read more button, when the line-clamp is applied. Now, how do I know the line-clamp is applying to this text and I have to show the readmore button?
Here is what I did to achieve this:
#foreach($module->captions as $caption)
<div class="mt-1 relative">
<div id="body-{{$caption->id}}" class="text-md leading-7 text-gray-700 font-normal whitespace-pre-line line-clamp-6">
{{ $caption->body }}
</div>
</div>
<div id="readMore-{{$caption->id}}" class="hidden flex justify-center absolute bottom-0 left-0 bg-white w-full rounded-b-lg">
<p class="text-blue-600 cursor-pointer py-2"></p>
</div>
#endforeach
<script>
let elements = document.getElementsByClassName('line-clamp-6')
Array.from(elements).forEach((element) => {
let captionId = element.id.split('-')[1];
let body = document.getElementById('body-' + captionId)
let readMore = document.getElementById('readMore-' + captionId)
if (element.clientHeight === 168) {
readMore.firstElementChild.innerText = 'Read More'
readMore.classList.remove('hidden')
readMore.addEventListener('click', (el) => {
if (body.classList.contains('line-clamp-6')) {
body.classList.remove('line-clamp-6')
readMore.firstElementChild.innerText = 'Read Less'
} else {
body.classList.add('line-clamp-6')
readMore.firstElementChild.innerText = 'Read More'
}
})
}
})
</script>
In my case I'm using a foreach to loop through some data but you should still be able to customize this to your needs. Basically, I'm getting all the elements that have a class name of line-clamp-6. Then were looping through those elements to see if they have a clientHeight of 168. If so, they need the Read More link.
Keep in mind you might need to check for a different clientHeight.
Then I listen for a click on the read more link if line-clamp-6 is present then we remove it else we add it.
Hopefully this helps you. I through this together in the last hour so there may be a better way to do it but this seems to work for me.
hey I am building an spa app with vuejs 3 and I am stuck with v-if and v-else not working as intended
<div v-if="this.isScroll" #click = "toggleIsScroll" class ="spinner-container ">
<i ref = "icon" class = "bi bi-pause-fill icon"> </i>
<div ref="spinner" class ="spinner-border text-dark loader "> </div>
</div>
<div v-else #click = "toggleIsScroll" class ="spinner-container ">
<i ref = "icon" class = "bi bi-play-fill icon"> </i>
<div ref="spinner" class ="spinner-border text-dark loader visually-hidden"> </div>
</div>
the docs says that v-else needs to come right after v-if but I don't know if it works if v-if has children inside it
i have tried to create the v-if section and v-else section in separate components and use v-if and v-else on those as follows
<CustomComponent v-if="this.IsScroll"/>
<SecondCustomComponent v-else/>
but it just renders both components always so how should I go about this?
can you use v-else on followed v-if element even if the v-if one has child elements before the v-else?
When it comes to displaying components conditionally, it would be best to go with v-show since it won't re-render every time when you make it hide/show
<CustomComponent v-show="this.IsScroll"/>
<SecondCustomComponent v-show="!this.IsScroll"/>
v-if and v-else should work with children inside the element. Also you don't need to do v-if="this.isScroll" but just v-if="isScroll".
Assuming that IsScroll is set in the data, the this is not required.
Use v-show if you need to toggle something often, and use v-if if the condition is unlikely to change at runtime.
data: {
IsScroll: true
},
methods:{
toggleIsScroll(){
.....
}
}
<div v-if="isScroll" #click= "toggleIsScroll" class ="spinner-container ">
<i ref = "icon" class = "bi bi-pause-fill icon"> </i>
<div ref="spinner" class ="spinner-border text-dark loader "> </div>
</div>
<div v-else #click= "toggleIsScroll" class ="spinner-container ">
<i ref = "icon" class = "bi bi-play-fill icon"> </i>
<div ref="spinner" class ="spinner-border text-dark loader visually-hidden"> </div>
</div>
So though my question might sound familiar the case is a bit different. I have a screen with multiple tasks. To show the tasks I am iterating via the data and my code looks something like
<div *ngFor="let task of tasks" class="scheduleContainer inline-block shadow">
<div id="myHeader" #myHeader class="activeHeader">
{{task.title}}
</div>
<div class="detailsBox">
<div class="row">
<div class="offset-md-1 col-md-auto">
Last Date:
</div>
<div class="col-md-auto">
{{task.lastDate}}
</div>
</div>
<div class="row">
<div class="offset-md-1 col-md-auto">
Duration:
</div>
<div class="col-md-auto">
{{task.duration}}
</div>
</div>
<div class="row">
<div class="offset-md-1 col-md-auto">
Total Runs:
</div>
<div class="col-md-auto">
{{task.totalRun}}
</div>
</div>
</div>
<div class="footer">
<a [routerLink]="['edit-scheduled-tasks']">edit schedule</a>
<a [routerLink]="['view-history-scheduled-tasks']">view history</a>
<a (click)="onClick()">enable task</a>
run now
</div>
</div>
<router-outlet></router-outlet>
Now when I click on the enabled task, I would like the color of that particular div to be changed. In my component, I tried something like
onClick() {
this.myHeader.nativeElement.style.background = 'red';
}
So this did change the color but it did not change the current task but rather some other task. Suggestions?
you can access myHeader from template so you can change the color something like this
<div id="myHeader" #myHeader class="activeHeader">
Change the color by myHeader variable
</div>
<button (click)="myHeader.style.background='red'">click</button>
or you can use a property with ngStyle like this
<div [ngStyle]="{'background-color':color}" >
Another way by ngStyle
</div>
<button (click)="color='red'">click</button>
or you can use a property to toggle class with ngClass
<div [ngClass]="{'red':isClicked}" >
Set class
</div>
<button (click)="isClicked=!isClicked">Toggle class</button>
Example toggle color of taskList by useing ngClass
template
<div *ngFor="let task of taskList"
[ngClass]="{'red':selectedTasks[task.id]}"
(click)="selectedTasks[task.id]= !selectedTasks[task.id]" class="task">
{{task.name}}
</div>
or you can use button to toggle the state
<div *ngFor="let task of taskList"
[ngClass]="{'red':selectedTasks[task.id]}"
class="task">
{{task.name}}
<button (click)="selectedTasks[task.id]= !selectedTasks[task.id]">toggle {{task.name}}</button>
</div>
if you want to set the state without toggle on click event just set
the state to true like this selectedTasks[task.id] =true
component
taskList =[
{id:1 , name:'Task 01'},
{id:2 , name:'Task 02'},
{id:3 , name:'Task 03'},
{id:4 , name:'Task 04'},
{id:5 , name:'Task 05'},
];
selectedTasks = {};
stackblitz demo
Not a clean way to do, but it still works. Send an index of selected element to onClick(i) and add the color to selected div. So that you don't mess with template reference.
html
<div *ngFor="let task of tasks; let i=index;" class="scheduleContainer inline-block shadow">
<div id="myHeader" #myHeader class="activeHeader">
{{task}}
</div>
<div class="footer">
<a (click)="onClick(i)">enable task</a>
</div>
</div>
component.ts
onClick(index: number) {
document.querySelectorAll('#myHeader')[index]
.style.background = 'red';
}
DEMO
It's not a good practice to manipulate DOM directly.
Angular: Stop manipulating DOM with ElementRef!
As an alternate, It's easy to bind inline style in your Angular templates using style binding.
Since you would like the color of that particular div to be changed. Use A boolean array:
component:
export class AppComponent {
name = 'Angular';
public styleArray=new Array<boolean>(10);
onClick(i)
{
this.styleArray[i]=true;
}
}
While Iterating pass index to onClick(i) to set particular index of array true and apply style dynamically
[style.color]="styleArray[i] ? 'green':'black'"
<div *ngFor="let task of tasks; let i=index" class="scheduleContainer inline-block shadow">
<div id="myHeader" [style.color]="styleArray[i] ? 'green':'black'" class="activeHeader">
{{task.title}}
</div>
........rest of the code
<a (click)="onClick(i)">enable task</a>
Live Demo