[{"content":" This article is published on my personal website. Original link: Nuxt Learning Journey 1\nIntroduction In the era of AI, being a simple \u0026ldquo;UI slicer\u0026rdquo; or having only basic knowledge of frontend engineering is no longer a high-value skill. To avoid being left behind by the rapidly advancing wheels of technology, I’ve decided to learn full-stack development while I\u0026rsquo;m still young. Since there are relatively few comprehensive Nuxt tutorials in Chinese, I\u0026rsquo;ve started this series to document my learning and help others avoid common pitfalls.\nInstalling Nuxt I am using WebStorm to create the project directly. For beginners, convenience and avoiding initial setup errors should be the top priorities. Of course, creating a project with other IDEs is also simple. The following command creates a project using the latest version of Nuxt:\n1 npm create nuxt@latest your-project-name The subsequent process is the same regardless of the IDE. Nuxt will start an interactive configuration flow. Once completed, you can start the development server with npm run dev.\n1 npm run dev And just like that, our first Nuxt project is up and running!\nConvention Over Configuration A key feature of Nuxt is Convention Over Configuration. In theory, we don\u0026rsquo;t need to write any configuration files; Nuxt works according to predefined conventions. For example, we don\u0026rsquo;t need to configure routes manually. By simply creating files in the pages directory, Nuxt automatically recognizes them and creates the corresponding routes.\nLet\u0026rsquo;s try adding a login page:\nCreating Pages First, we create a pages directory inside the app directory. Create app/pages/index.vue and app/pages/login.vue, then add the relevant content. At this point, if we delete app.vue, Nuxt will automatically render index.vue as the homepage.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;script lang=\u0026#34;ts\u0026#34;\u0026gt; import {defineComponent} from \u0026#39;vue\u0026#39; export default defineComponent({ name: \u0026#34;index\u0026#34; }) \u0026lt;/script\u0026gt; \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;NuxtLink to=\u0026#34;/login\u0026#34;\u0026gt;Go to Login Page\u0026lt;/NuxtLink\u0026gt; \u0026lt;h2\u0026gt;This is the Homepage\u0026lt;/h2\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;style scoped\u0026gt; \u0026lt;/style\u0026gt; Default Layout So, what is app.vue? It is our default layout file, effectively the parent component for all pages. While deleting it lets Nuxt render index.vue by convention, let’s keep it and write some layout code:\n1 2 3 4 5 6 \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;h1\u0026gt;This is the Default Layout!\u0026lt;/h1\u0026gt; \u0026lt;NuxtPage /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; If Nuxt finds app.vue, it will use it to wrap and render index.vue as the homepage. Our page and project structure now look like this:\nCreating a Web API Nuxt’s network interfaces are also convention-based and written in JavaScript, which is very friendly for frontend developers. Create a server directory in the project root (note: not inside the app directory), then create a server/api/hello.ts file. Nuxt will automatically create the /api/hello endpoint.\nLet\u0026rsquo;s write a simple API to calculate the sum of two numbers:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 export default defineEventHandler((event) =\u0026gt; { // Get query parameters num1 and num2 const query = getQuery(event) const num1 = Number(query.num1) const num2 = Number(query.num2) if(isNaN(num1) || isNaN(num2)) { // If parameters are not numbers, throw an error throw createError({ status: 500, statusText: \u0026#34;Invalid query number\u0026#34;, }) } else { // Return the calculation result return num1 + num2 } }) Using the API in the Frontend Let’s try calling this API in app/pages/index.vue. This code might look confusing at first glance, but I will explain it in parts below.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 \u0026lt;script lang=\u0026#34;ts\u0026#34;\u0026gt; import {defineComponent} from \u0026#39;vue\u0026#39; export default defineComponent({ name: \u0026#34;index\u0026#34;, data(): any { // This data, once processed by the server, will be sent to the frontend with the page return { result: 0, num1: 0, num2: 0, } }, created(): any { // This part of the code executes on the server const route = useRoute() if(route.query.num1){ this.num1 = route.query.num1 } if(route.query.num2){ this.num2 = route.query.num2 } const data = useFetch(\u0026#39;/api/hello\u0026#39;,{ query: { num1: this.num1, num2: this.num2 } }) this.result = data.data }, methods: { // This code executes in the browser async plus(){ this.result = await $fetch(\u0026#39;/api/hello\u0026#39;,{ query: { num1: this.num1, num2: this.num2 } }) } } }) \u0026lt;/script\u0026gt; \u0026lt;template\u0026gt; \u0026lt;div\u0026gt; \u0026lt;NuxtLink to=\u0026#34;/login\u0026#34;\u0026gt;Go to Login Page\u0026lt;/NuxtLink\u0026gt; \u0026lt;h2\u0026gt;This is the Homepage\u0026lt;/h2\u0026gt; \u0026lt;input v-model=\u0026#34;num1\u0026#34;/\u0026gt; + \u0026lt;input v-model=\u0026#34;num2\u0026#34;/\u0026gt; = {{result}} \u0026lt;button @click=\u0026#34;plus\u0026#34;\u0026gt;Calculate\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;style scoped\u0026gt; \u0026lt;/style\u0026gt; Let\u0026rsquo;s look at the server-side logic first:\n1 2 3 4 5 6 7 8 9 10 11 12 13 // This part of the code executes on the server // useRoute() accesses route parameters const route = useRoute() if(route.query.num1){ this.num1 = route.query.num1 } if(route.query.num2){ this.num2 = route.query.num2 } // useFetch() is used to call APIs internally on the server const data = useFetch(\u0026#39;/api/hello\u0026#39;,{ query: { num1: this.num1, num2: this.num2 } }) // Assigning to data; Nuxt will render this into the page this.result = data.data In Nuxt, the beforeCreate and created lifecycles are run on the server. We can pre-assemble the page on the server, much like PHP.\nAs you can see, when we visit the page with parameters, the server returns the page with the result already calculated, unlike a traditional SPA that fetches an empty page and then fills in the data.\nNow, let\u0026rsquo;s look at the browser-side code:\n1 2 3 4 5 6 7 8 9 methods: { // This code executes in the browser async plus(){ // $fetch() is the data request function provided by Nuxt this.result = await $fetch(\u0026#39;/api/hello\u0026#39;,{ query: { num1: this.num1, num2: this.num2 } }) } } This is no different from an SPA. Binding this method to a button triggers a network request directly from the browser.\nSummary Today, we set up and experienced Nuxt for the first time. We created two pages and one API, using them on both the frontend and backend. We\u0026rsquo;ve caught a glimpse of Nuxt\u0026rsquo;s powerful server-side rendering capabilities and its seamless connection between the frontend and backend.\n","date":"2026-04-06T00:00:00Z","image":"https://blog.zhoujump.com/en/p/learn-nuxt-1/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/learn-nuxt-1/","title":"Nuxt Learning Journey 1"},{"content":" This article was originally published on my personal website: Deleting Cloudflare Pages projects with many deployments\nSmall number of deployments If your deployment count is only one or two hundred, you can try manually deleting old deployments. Delete until the deployment count is less than one hundred, and then you should be able to delete the project. Large number of deployments You need to use Cloudflare\u0026rsquo;s API to delete deployments, and you need to install Cloudflare CLI to perform this operation.\nThis part has been covered by others in tutorials, so I won\u0026rsquo;t repeat it here. You can refer to this article.\n","date":"2026-01-02T00:00:00Z","image":"https://blog.zhoujump.com/en/p/delete-cloudflare-project/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/delete-cloudflare-project/","title":"Deleting Cloudflare Pages projects with many deployments"},{"content":" This article is published on my personal website, original link: A CSS-Only Scroll-Driven Animation Effect\nFirst, let\u0026rsquo;s look at this simple example Code: You can also scroll down to see the effect directly.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 \u0026lt;div class=\u0026#34;out-cont\u0026#34;\u0026gt; \u0026lt;!-- The outermost element, used to define the fixed distance of the element --\u0026gt; \u0026lt;div class=\u0026#34;inner-cont\u0026#34;\u0026gt; \u0026lt;!-- Inner element, used to fix the position of the element --\u0026gt; \u0026lt;div class=\u0026#34;animation-item\u0026#34;\u0026gt; \u0026lt;!-- The content of the element that plays the animation --\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .out-cont { /* The outermost element, used to define the fixed distance of the element, which means the element will be fixed for two screen heights */ width: 100%; height: 200vh; position: relative; } .inner-cont { /* Inner element, used to fix the position of the element, using sticky to fix the element within the range */ width: 100%; height: 50vh; position: sticky; top: 25vh; display: flex; align-items: center; justify-content: center; } .animation-item{ /* The content of the element that plays the animation, using animation-timeline to achieve scroll-driven animation */ width: 20%; aspect-ratio: 1; background-color: #2E74B5; animation: move 1s linear forwards; animation-timeline: view(); animation-range: contain; } @keyframes move { 0% { transform: rotate(0deg); border-radius: 8%; } 50% { transform: rotate(360deg); border-radius: 50%; } 100% { border-radius: 8%; transform: rotate(720deg); } } \u0026lt;/style\u0026gt; Effect: .out-cont .inner-cont Viewport sticky: Sticky Layout For front-end developers, this is not unfamiliar, so I\u0026rsquo;ll just briefly introduce it: position: sticky is a layout method between position:relative and position:fixed. When the parent element appears on the screen, it behaves like fixed, fixing itself on the screen. When the parent element goes out of the screen, it behaves like relative, following the normal document flow layout, being taken away by the parent container. Regarding how this case uses sticky to achieve similar element fixing effects, there is a simple demonstration on the right side of the effect example above, which can help with understanding. And this demonstration is also pure CSS.\nAll parent elements of a sticky element cannot set overflow:hidden, as this will invalidate sticky. If really needed, you can use overflow:clip instead.\nanimation-timeline: Scroll-Driven Animation This is the core of this case. When this property is used on an element, the CSS animation defined by @keyframes will not play automatically, but will scroll according to the progress of the scrollbar. This property has two main values:\nanimation-timeline: view() The animation starts when the element enters the viewport and ends when it leaves the viewport. animation-timeline: scroll() The animation plays when the element scrolls within the entire scroll container. animation-timeline: cont-name Named container, which will be discussed later. Viewport view() Viewport scroll() animation-timeline: scroll() scroll() is for the entire page and is relatively simple. It is used for effects related to global scrolling, such as article reading progress. Here I quote a picture from 前端侦探.\nscroll() can take two parameters: scroller and axis\nscroller The scroller parameter is used to specify the scroll container, with the default value being nearest. If set to nearest, the nearest ancestor scroll container will be used. If set to root, the document viewport will be used as the scroll container. If set to self, the element itself will be used as the scroll container.\naxis The axis parameter is used to specify the scroll axis, with the default value being block. If set to block, the block-level axis direction of the scroll container. If set to inline, the inline axis direction of the scroll container. If set to x, the element will scroll horizontally. If set to y, the element will scroll vertically.\nanimation-range If I don\u0026rsquo;t want the animation to play throughout the entire scrolling period, I can use the animation-range property to set the start and end positions of the animation, with px and % units both acceptable. For example:\n1 2 3 .animation{ animation-range: 0 100px; } This way, the animation will only play when the scroll container scrolls to the position of 0 to 100px, and will not play after 100px.\nanimation-timeline: view() view() is relative to the position of the element and the viewport, and this case is based on view(). view() can also take two parameters: axis and inset\naxis The axis parameter is used to specify the scroll axis, with the default value being block. If set to block, the block-level axis direction of the scroll container. If set to inline, the inline axis direction of the scroll container. If set to x, the element will scroll horizontally. If set to y, the element will scroll vertically.\ninset The inset parameter is used to specify when the animation starts and ends, whether it starts when the element just peeks out or when the element completely enters the viewport, which is controlled by this property, somewhat similar to the role of animation-range above. inset accepts one or two values. When there are two values, they represent the start position and end position, with px and % units both acceptable.\nanimation-timeline: cont-name You may have noticed that with the above properties alone, we can only achieve animations where the parent element scrolls to drive the child element. What if I need to scroll one container to drive the animation of another sibling element? Then we need to rely on named containers. It\u0026rsquo;s very simple to use. Just use an attribute scroll-timeline-name on the scroll container.\n1 2 3 4 5 6 7 8 .scroll{ /* Naming the scroll container */ scroll-timeline-name: --my-scroller; } .animation{ /* The element that needs to be driven by the animation */ animation-timeline: --my-scroller; } In this way, when the .scroll container is scrolled, the .animation element will play the animation according to the scroll progress of the scroll container.\nRelated Knowledge position:fixed animation-timeline animation-range 前端侦探\n","date":"2025-09-14T00:00:00Z","image":"https://blog.zhoujump.com/en/p/animation-timeline/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/animation-timeline/","title":"A CSS-Only Scroll-Driven Animation Effect"},{"content":" This article is published on my personal website, original link: Two-way data binding of contenteditable elements in Vue\nA record of what to pay attention to when using the contenteditable attribute in Vue.\ncontenteditable Usage Adding contenteditable=\u0026ldquo;true\u0026rdquo; attribute to any element can make the element editable. It can achieve richer editing control layout than ordinary input and textarea. For example, an editor that can embed tags like this: Features If the contenteditable attribute is only added without assigning a value, it will be regarded as contenteditable=\u0026ldquo;false\u0026rdquo;. If there is an element with contenteditable=\u0026ldquo;false\u0026rdquo; in a container with contenteditable=\u0026ldquo;true\u0026rdquo;, then the element itself cannot be edited, but pressing backspace can delete the entire element. If there is an ordinary element in a container with contenteditable=\u0026ldquo;true\u0026rdquo;, then pressing backspace will delete the text according to the HTML structure. Until the innermost element has no text, the element tag itself will be deleted.\nAbout Vue binding v-model v-model essentially automatically binds the input event and value attribute. Although contenteditable will give the element an input event, since the contenteditable attribute is needed, this element is likely to have no value attribute. So in most cases, v-model can only pass the element content to Vue, but Vue cannot update the element content. At this time, if you naturally bind the value:\n1 \u0026lt;span contenteditable=\u0026#34;true\u0026#34; v-model=\u0026#34;item.value\u0026#34; v-html=\u0026#34;item.value\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; Or\n1 \u0026lt;span contenteditable=\u0026#34;true\u0026#34; v-model=\u0026#34;item.value\u0026#34;\u0026gt;{{item.value}}\u0026lt;/span\u0026gt; You will find that you cannot input normally at all. Every time a key is pressed, the cursor will return to the beginning of the element. If you use Pinyin input method, the problem will be even more bizarre. This is because the input event is triggered every time a key is pressed, and every time a key is pressed, all the content currently seen will be given to Vue for processing. At the same time, the content marked by the template syntax {{}} or v-html will be updated by Vue, and the entire element\u0026rsquo;s content will be replaced, so it is naturally impossible to input normally. The solution is very simple, we should not use v-model or v-input to update data, but should choose a more appropriate time to update data, such as when the input box loses focus.\n1 \u0026lt;span contenteditable=\u0026#34;true\u0026#34; @blur=\u0026#34;item.value=$event.target.innerText\u0026#34;\u0026gt;{{item.value}}\u0026lt;/span\u0026gt; Unexpectedly, the input box can now bind data two-way normally.\nRelated knowledge contenteditable v-model blur\n","date":"2025-08-26T00:00:00Z","image":"https://blog.zhoujump.com/en/p/contenteditable-vue/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/contenteditable-vue/","title":"Two-way data binding of contenteditable elements in Vue"},{"content":" Original article: How to embed UnityWeb3D into a page\nWith the improvement of equipment performance now, it is no longer a rare thing to insert 3D models into the website. Moreover, inserting 3D elements can greatly improve the user experience, and some user interactions can give people a very amazing experience. Currently, the most popular Web3D technologies include three.js, visual editing models and interactive splines, but the difficulty of learning three.js and the high price of spline are enough to discourage some people. So are there more economical and simpler technologies? Some are some, some are some, and UnityWeb3D may be a good choice.\nProject preparation Unity\u0026rsquo;s scene construction and post-processing are very simple, without writing code and shaders. Specific operation methods: There are so many tutorials to learn at B station University. I have prepared an earth here and added some filters to it. Interaction is also very simple, just write some interactive logic through visual scripts. You can also go to Bilibili University for an elective. After you have prepared the project, select it in turnEdit\u0026gt;Build Settings\u0026gt;WebGL\u0026gt;Switch Platform, switch the project to the WebGL platform, and the progress bar will be passed once during the unity. I\u0026rsquo;ve switched here, so the Generate button is displayed. After waiting for the switch to complete, you can make some configurations of the generation settings and selectEdit\u0026gt;Project Settings\u0026gt;Player\u0026gt;H5. Choose configuration according to your needs. Unit uses gzip compression by default, which can greatly reduce the generation size, but we need to configure gzip on the server. Fu Ge, who thinks it’s troublesome and does not lack traffic bandwidth, can turn it off. Then click Generate, unity will let you select a folder to save the generated files. I have created a new dist folder here to place the generated files. After waiting for unity generation to complete, an index.html and a Build folder will be generated. Embed UnityWeb3D Let\u0026rsquo;s upload all the files in the Build folder to the server. There are four files in total. I\u0026rsquo;ve put them here.assets/unityAnimationUnder the folder. Then, the page will be introduced first.dist.loader.jsThe file is then prepared and an id is given to us so that we can get it in js.\n1 2 \u0026lt;script src=\u0026#34;/assets/unityAnimation/dist.loader.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;canvas id=\u0026#34;unity-canvas\u0026#34;\u0026gt;\u0026lt;/canvas\u0026gt; Then usecreateUnityInstance()Function to create UnityWeb3D instances. In the configuration, these paths can be used by changing them to the file paths you upload. Other configurations are in the official documentation:WebGL templatesYou can view it here. Because I enable gzip compression, I need to add the .gz suffix here. If you do not enable gzip in the generation settings, you do not need to add a suffix. After refreshing the page, you will find that the model is loading correctly. If gzip is enabled, gzip needs to be configured on the server.\n1 2 3 4 5 createUnityInstance(document.querySelector(\u0026#34;#unity-canvas\u0026#34;), { dataUrl: \u0026#34;/assets/unityAnimation/dist.data.gz\u0026#34;, frameworkUrl: \u0026#34;/assets/unityAnimation/dist.framework.js.gz\u0026#34;, codeUrl: \u0026#34;/assets/unityAnimation/dist.wasm.gz\u0026#34;, }); My server is using Nginx, so I need to add the following code to the Nginx configuration file to enable gzip. The advantage of using gzip is to reduce the resource size. I have a total of 30M before the compression of this earth, and only 8M after compression. It is very amazing. Of course, if you choose other compression methods or servers, in the official document:WebGL: server configuration code sampleThere are also detailed instructions.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 location ~ .+\\.(data|symbols\\.json)\\.gz$ { gzip on; add_header Content-Encoding gzip; default_type application/octet-stream; } location ~ .+\\.js\\.gz$ { gzip on; add_header Content-Encoding gzip; default_type application/javascript; } location ~ .+\\.wasm\\.gz$ { gzip on; add_header Content-Encoding gzip; default_type application/wasm; } See the effect In this way, I, a front-end person who doesn\u0026rsquo;t understand Unity3D and C# at all, can also achieve such a super cool effect. ","date":"2025-04-18T00:00:00Z","image":"https://blog.zhoujump.com/en/p/insert-unity/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/insert-unity/","title":"How to embed UnityWeb3D into a page"},{"content":" Original article: Write a Tiny Vue - Getter and Setter\nI believe that all the front-end experts and novices have heard this statement:\nVue 2 uses getters and setters to control data, while Vue 3 directly uses proxy.\nSurely, there are many people like me who just want to enjoy using Vue without caring about getters or proxies. However, these things are very practical in actual development and can achieve some very elegant encapsulations. But first, let\u0026rsquo;s get to know these two things.\nGetter and setter Getter and setter are like listeners. When a certain piece of data is modified or read, the getters and setters you have set will run.\nFor example, I have now defined a box, and the content inside it is a \u0026lsquo;banana🍌\u0026rsquo;.\n1 2 3 let box = { content: \u0026#39;banana🍌\u0026#39; } Let\u0026rsquo;s try to monitor this box.\n1 2 3 4 5 6 7 8 9 10 11 12 let box = { content: \u0026#39;banana🍌\u0026#39;, //Defining a getter is very simple; it\u0026#39;s just a function, and the function name is the variable that needs to be listened to. get boxContent() { alert(\u0026#39;Someone has looked inside the box. It contains \u0026#39; + this.content + \u0026#39;.\u0026#39;); }, //Defining a setter is the same. set boxContent(newContent) { this.content = newContent alert(\u0026#39;Someone has replaced the contents of the box with \u0026#39; + newContent) } } One point to note here is that the names of getters and setters cannot be the same as the original variable. However, in the Firefox browser, this is allowed. But for compatibility reasons, we do not recommend doing so.\nAt this point, as soon as someone attempts to read from or modify boxContent, the getter and setter will be triggered.\n1 2 3 4 5 //If there is something in the box if(box.boxContent){ //Just replace it with an apple. box.boxContent = \u0026#39;apple🍎\u0026#39; } The first line\u0026rsquo;s if reads box.boxContent, triggering the getter, and then a prompt pops up. Immediately after we modified box.boxContent, the setter was triggered and a prompt popped up. Great! You\u0026rsquo;ve learned getters and setters. Let\u0026rsquo;s implement a small Vue now.\nImplementing a Tiny Vue Let\u0026rsquo;s take a classic example: Xiaohei\u0026rsquo;s Notepad: I won\u0026rsquo;t elaborate on the style and layout as they are quite simple. You can check the specific code on GitHub or on code.juejin.\nIt\u0026rsquo;s said to be a mini Vue implementation, but in reality, it\u0026rsquo;s just an attempt to replicate the v-model functionality through getters and setters. After all, we\u0026rsquo;re not Evan You, so let\u0026rsquo;s keep it simple.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 \u0026lt;! DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;style.css\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Demo\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;outter\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;title\u0026#34;\u0026gt;Demo\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;list\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;item\u0026#34;\u0026gt; \u0026lt;span\u0026gt;2333\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026#34;delete\u0026#34;\u0026gt;Delete\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;add\u0026#34;\u0026gt; \u0026lt;!-- We set a bind attribute, which is equivalent to v-model, and bind it to the input variable --\u0026gt; \u0026lt;input bind=\u0026#34;input\u0026#34;/\u0026gt; \u0026lt;button onclick=\u0026#34;add()\u0026#34; class=\u0026#34;add-btn\u0026#34;\u0026gt;Add\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Here\u0026#39;s another bind, also bound to the input variable --\u0026gt; \u0026lt;div\u0026gt;The value of input: \u0026lt;span bind=\u0026#34;input\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;script src=\u0026#34;index.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/html\u0026gt; The JavaScript part is as follows. We only focus on the first part.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 //Let\u0026#39;s define the data first. let data = { list:[], input:\u0026#39;\u0026#39; } // Implement binding // Here, obtain all elements that have the \u0026#34;bind\u0026#34; attribute let binds = document.querySelectorAll(\u0026#39;[bind]\u0026#39;); // Traverse these elements binds.forEach((item) =\u0026gt; { // Bind the input event item.oninput = (e) =\u0026gt; { //Update its value when input. data[item.getAttribute(\u0026#39;bind\u0026#39;)] = e.target.value } }) // Implement data listening let myData = {} //Since getters and setters cannot have the same name as the original variable, let\u0026#39;s wrap the original variable up. myData.data = data //We iterate through each object in data. for(let item in data){ //Define a getter and setter for each item. Object.defineProperty(myData, item, { //Here, getters and setters are set through defineProperty. get(){ return myData.data[item] }, set(val){ myData.data[item] = val //When the value is updated, we traverse all elements with the bind attribute. binds.forEach((bindsItem) =\u0026gt; { //If this element has a bind attribute and the attribute name is equal to \u0026#34;item\u0026#34;, modify its value. if(bindsItem.getAttribute(\u0026#39;bind\u0026#39;) === item){ bindsItem.value = val bindsItem.innerHTML = val } }) //When the list is updated, re-render the list. if(item === \u0026#39;list\u0026#39;) { renderList() } } }) } //Assign `myData` to `data`, and in this way, our `data` becomes a proxy object. data = myData // Those interested can take a look at the following code. // Render list function function renderList() { const listContainer = document.querySelector(\u0026#39;.list\u0026#39;) listContainer.innerHTML = data.list.map((item, index) =\u0026gt; ` \u0026lt;div class=\u0026#34;item\u0026#34;\u0026gt; \u0026lt;span\u0026gt;${item}\u0026lt;/span\u0026gt; \u0026lt;button class=\u0026#34;delete\u0026#34; onclick=\u0026#34;deleteItem(${index})\u0026#34;\u0026gt;Delete\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; `).join(\u0026#39;\u0026#39;) } // Function to add a project function add() { if(data.input.trim()) { data.list = [...data.list, data.input] data.input = \u0026#39;\u0026#39; // Clear the input box } } // Function to delete an item function deleteItem(index) { data.list = data.list.filter((_, i) =\u0026gt; i ! == index) } Let\u0026rsquo;s take a look at the effect In this way, we have implemented a v-model using native JavaScript. Don\u0026rsquo;t you feel a great sense of achievement? Relevant Knowledge getter setter defineProperty\n","date":"2025-03-13T00:00:00Z","image":"https://blog.zhoujump.com/en/p/getter-setter/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/getter-setter/","title":"Write a Tiny Vue - Getter and Setter"},{"content":" Original article: Pure CSS to achieve the marquee effect\nSee the effect first I am a personal intern who has been practicing marketing for two and a half years. I like html, css, and js. I am a personal intern who has been practicing marketing for two and a half years. I like html, css, and js. \u0026lt;marquee\u0026gt; tag At this time, some front-end bosses will stand up and say, isn\u0026rsquo;t this just a matter of \u0026lt;marquee\u0026gt; tag? However, this element has been abandoned. Although it can still be used, it may be removed by mainstream browsers one day. It is better to use it less Use CSS animation Apply animation to text Since the marquee simply scrolls the text horizontally, we can use CSS animation to achieve it. Let\u0026rsquo;s set a piece of text first, and then apply animation to it.\nI am a personal intern who has been practicing the market for two and a half years. I like html, css, and js. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \u0026lt;div class=\u0026#34;marquee-1\u0026#34;\u0026gt; \u0026lt;span\u0026gt;I am a personal intern who has been practicing the market for two and a half years. I like html, css, and js. \u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .marquee-1 { overflow: hidden; width: 300px; display: flex; } .marquee-1 span { animation: marquee 10s linear infinite; } @keyframes marquee { 100% {transform: translateX(-100%);} } \u0026lt;/style\u0026gt; It looks like the text wraps after it exceeds the container.\nForce the text to not wrap Then we force the text to not wrap. See the effect\nI am a personal intern who has been practicing marketing for two and a half years. I like html, css, and js. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;div class=\u0026#34;marquee-2\u0026#34;\u0026gt; \u0026lt;span\u0026gt;I am a personal intern who has been practicing marketing for two and a half years. I like html, css, and js. \u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .marquee-2 { overflow: hidden; /* You can force the text not to wrap here */ white-space: nowrap; width: 300px; display: flex; } .marquee-2 span { animation: marquee 10s linear infinite; } @keyframes marquee { 100% {transform: translateX(-100%);} } \u0026lt;/style\u0026gt; Now it looks a bit interesting, that is, the text will not start to scroll again until it is completely scrolled.\nCopy an element to achieve seamless scrolling We can copy an element and then let the animation run at the same time, so that it looks like seamless scrolling. The complete code is below.\nI am a personal intern who has been practicing marketing for two and a half years. I like html, css, and js. I am a personal intern who has been practicing the market for two and a half years. I like html, css, and js. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;div class=\u0026#34;marquee\u0026#34;\u0026gt; \u0026lt;span\u0026gt;I am a personal intern who has been practicing the market for two and a half years. I like html, css, and js. \u0026lt;/span\u0026gt; \u0026lt;span\u0026gt;I am a personal intern who has been practicing the market for two and a half years. I like html, css, and js. \u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .marquee{ overflow: hidden; white-space: nowrap; width: 400px; display: flex; } .marquee span{ animation: marquee 10s linear infinite; } @keyframes marquee{ 100% {transform: translateX(-100%);} } \u0026lt;/style\u0026gt; Related knowledge marquee: marquee element white-space\n","date":"2025-02-20T00:00:00Z","image":"https://blog.zhoujump.com/en/p/css-marquee/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/css-marquee/","title":"Pure CSS to achieve the marquee effect"},{"content":" Original article: Recording a blog site migration\nCause Because one day when I logged into gitlab, I found that I was issued a 60-day death notice, and the account would be deleted in two months, so the site relocation was put on the agenda. Of course, it is impossible to krypton gold, so I set my sights on the cyber living Buddha cloudflare.\nSteps Transfer warehouse Gitlab wants to delete my warehouse, so the first priority is of course to transfer the warehouse first, and I am transferring it to github here. The process is very simple. Select Import Repository and fill in the original repository address and account password. Configure Cloudflare Pages The operation is also very simple. Prepare a cloudflare account in advance, open the pages page, link to git, and log in. Then select the corresponding repository, select the corresponding framework preset, and click Next to start building.\nIf an error occurs during the build, it may be a problem with the hugo version. Add an environment variable named HUGO_VERSION and fill in the hugo version you are currently using.\nConfigure Domain Name After the build is complete, you can see this page. Enter the configuration page, select Custom Domain, click Set Custom Domain, and then enter the domain name. Because my domain name is in cloudflare, it can take effect directly. If the domain name is in other domain name providers, just follow the prompts to add a cname resolution.\nAt this point, the transfer has been completed. It is still the original domain name and the original website, but the website has been quietly moved.\n","date":"2025-02-04T00:00:00Z","image":"https://blog.zhoujump.com/p/gitlab-to-cloudflare/cover.webp","permalink":"https://blog.zhoujump.com/en/p/gitlab-to-cloudflare/","title":"Recording a blog site migration"},{"content":" Original article: Pure CSS to implement circular progress bar\nSee the effect first This circular progress bar is made with pure CSS and uses CSS variables to control the progress. You can open the developer tool, select it and change its inline CSS variable --progress: 60, and both the progress and content will change.\nCSS variables CSS variables are a new feature introduced in CSS3, which allows you to define a variable. You can define a CSS variable through --variable name and use it through var(--variable name). The following small demo shows how to use CSS variables. You can open the developer tools and play with it.\nMy color is controlled by CSS variables 1 2 3 4 5 6 7 8 9 10 \u0026lt;!-- Define CSS variable color in style --\u0026gt; \u0026lt;div class=\u0026#34;demo-var\u0026#34; style=\u0026#34;--color:red\u0026#34;\u0026gt; My color is controlled by CSS variables \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-var{ /* Use color here */ color: var(--color); } \u0026lt;/style\u0026gt; We use a little trick here. Use CSS counter to display CSS variables. The number in the middle of the ring is displayed like this. You can open the developer tool and play with the small demo below.\n1 2 3 4 5 6 7 8 9 \u0026lt;div class=\u0026#34;demo-var\u0026#34; style=\u0026#34;--num:100\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-num::after{ /* Reset the num counter to the css variable you set */ counter-reset: num var(--num); /* Use the counter again to display it in disguise, but this method can only display numbers */ content: \u0026#39;The value of num is:\u0026#39; counter(num); } \u0026lt;/style\u0026gt; Cone gradient Using a conical gradient can produce a pizza shape, which is the key to our circular progress bar.\n1 2 3 4 5 6 7 8 9 10 11 12 13 \u0026lt;!-- We define a CSS variable by copying the same method --\u0026gt; \u0026lt;div class=\u0026#34;demo-conic\u0026#34; style=\u0026#34;--progress:60\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-conic{ margin: auto; position: relative; width: 200px; height: 200px; border-radius: 50%; /* Use CSS variables here and convert them to percentages through calc(var(--progress) * 1%) */ background: conic-gradient( #99e6ff 0%,#99e6ff calc(var(--progress) * 1%),transparent 0%); } \u0026lt;/style\u0026gt; The next step is very simple. We use a white circle to cover the conical gradient, and then use the trick mentioned above to display the progress number, so that we can get a circular progress bar.\nComplete code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 \u0026lt;!-- Create a progress bar container and define CSS variables --\u0026gt; \u0026lt;div class=\u0026#34;demo-process\u0026#34; style=\u0026#34;--progress: 60\u0026#34;\u0026gt; \u0026lt;!-- Add another circle inside to cover the pizza to form a ring --\u0026gt; \u0026lt;div class=\u0026#34;demo-process-inner\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-process{ margin: auto; position: relative; width: 200px; height: 200px; border-radius: 50%; padding: 16px; /* Use CSS variables to generate a pizza shape */ background: conic-gradient( #99e6ff 0%,#99e6ff calc(var(--progress) * 1%),transparent 0%); } .demo-process::after{ /* Let\u0026#39;s make a small circle to beautify the progress bar */ content: \u0026#39;\u0026#39;; position: absolute; left: calc(50% - 10px); top: calc(50% - 10px); width: 16px; height: 16px; border-radius: 50%; /* Control the position of the small circle through css variables to make it follow the progress */ transform: rotate(calc(3.6deg * var(--progress))) translateY(-92px); /* background: white; The css variable here is to adapt to the night mode, you can just use white*/ background: var(--card-background); border: 4px solid #99e6ff;; } .demo-process::before{ /* This is also used to beautify the progress bar */ content: \u0026#39;\u0026#39;; position: absolute; left: calc(50% - 8px); top: 0; width: 16px; height: 16px; border-radius: 50%; background: #99e6ff; } .demo-process-inner{ /* Inner circle to cover the pizza */ width: 100%; height: 100%; border-radius: 50%; display: flex; justify-content: center; align-items: center; /* background: white; This is also to adapt to the night mode, you can just use white*/ background: var(--card-background); } .demo-process-inner::before{ /* Display progress numbers */ counter-reset: process var(--progress); content: counter(process)\u0026#39;%\u0026#39;; font-size: 30px; color: #99e6ff; } \u0026lt;/style\u0026gt; Related knowledge css variables conic gradient css counter\n","date":"2024-12-15T00:00:00Z","image":"https://blog.zhoujump.com/en/p/circular-progression/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/circular-progression/","title":"Pure CSS to implement circular progress bar"},{"content":" Original article: Pure CSS to achieve a cool input box effect\nSee the effect first username :valid and :invalid pseudo-classes These two pseudo-classes can be used on input and select elements to select input boxes that have passed or failed validation. Let\u0026rsquo;s learn about them through the following simple example: This input box will check whether the input is an email address, otherwise the background color will be red warning.\nThe code is also very simple:\n1 2 3 4 5 \u0026lt;input class=\u0026#34;demo-input-2\u0026#34; type=\u0026#34;email\u0026#34;/\u0026gt; \u0026lt;style\u0026gt; //The invalid pseudo-class can select inputs that cannot pass the validation .demo-input-2:invalid{background:red} \u0026lt;/style\u0026gt; required attribute This attribute can be used on input and select elements to indicate that this input box is required. If left blank, it will fail the validation and can be selected by the :invalid pseudo-class, otherwise it will be selected by :valid, for example: This input box cannot be left blank, otherwise the background color will be red warning.\n1 2 3 4 \u0026lt;input class=\u0026#34;demo-input-3\u0026#34; type=\u0026#34;email\u0026#34;/\u0026gt; \u0026lt;style\u0026gt; .demo-input-2:invalid{background:red} \u0026lt;/style\u0026gt; pattern attribute This attribute can be used on input and select elements. Its attribute value needs to be filled in with a regular expression. If the content of the input box does not match the regular expression, it can be selected by the :invalid pseudo-class, otherwise it will be selected by :valid. For example: This input box can only enter six digits, otherwise the background color will be red warning.\n1 \u0026lt;input class=\u0026#34;demo-input-4\u0026#34; type=\u0026#34;email\u0026#34;/\u0026gt; \u0026lt;style\u0026gt; .demo-input-4:invalid{background:red} \u0026lt;/style\u0026gt; Complete code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 \u0026lt;div class=\u0026#34;demo-input\u0026#34;\u0026gt; \u0026lt;input required/\u0026gt; \u0026lt;label\u0026gt;username\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .demo-input{ position: relative; height: 40px; width: 300px; margin: 8px; } .demo-input *{ transition-duration: 100ms } .demo-input input{ width: 100%; height: 100%; box-sizing: border-box; border: 2px solid gray; border-radius: 6px; outline: none; padding-left: 10px; font-size: 20px; color: #006080; } .demo-input input:valid, .demo-input input:focus { border: 2px solid #006080 } .demo-input label{ position: absolute; top:0; left: 10px; font-size: 20px; color: gray; line-height: 36px; pointer-events: none; } .demo-input input:valid + label, .demo-input input:focus + label { color: #006080; top:-12px; font-size: 18px; padding: 0 4px; background-color: white; line-height: 18px; } \u0026lt;/style\u0026gt; Related knowledge Form data validation :valid pseudo-class :invalid pseudo-class required attribute pattern attribute\n","date":"2024-10-22T00:00:00Z","image":"https://blog.zhoujump.com/en/p/valid_input/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/valid_input/","title":"Pure CSS to achieve a cool input box effect"},{"content":" Original article: Cleverly use attribute selectors to implement filters in pure CSS\nTake a look at the effect of this example first All Yellow Red green Purple 🍉🍊🍈 🍇🥝🍋 ‍🍌🍍🥭 🍎🍏🍐 🍑🍒🍓 🫐‍🍅🫒 :has() pseudo-class and attribute selector Let\u0026rsquo;s learn about these two selectors through the following small example\nAttribute selector I am I am 1 2 3 4 5 6 7 \u0026lt;!-- Pseudo-elements will be added when the mouse touches them --\u0026gt; \u0026lt;div linkto=\u0026#39;box1\u0026#39;\u0026gt;I am\u0026lt;/div\u0026gt; \u0026lt;div linkto=\u0026#39;box2\u0026#39;\u0026gt;I am\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; div[linkto=\u0026#39;box1\u0026#39;]:hover::after{content:\u0026#39;box1\u0026#39;} div[linkto=\u0026#39;box2\u0026#39;]:hover::after{content:\u0026#39;box2\u0026#39;} \u0026lt;/style\u0026gt; In this example, the two divs do not have any class names, but are given a custom attribute linkto. We can use this to select them through [linkto='box1'] and assign pseudo elements.\nHow to use In addition to the exact match of a certain attribute value in this example, attribute selectors have many other uses:\nMatch attribute name img[alt] This selector will select all img elements with an alt attribute, regardless of the alt content.\nExactly match attribute img[hidden=\u0026quot;true\u0026quot;] This selector will select all img elements with a hidden attribute and a value of true. We can easily hide the element by adding the display:none style to this selector.\nMatch the existence of a certain attribute value img[tag~=\u0026quot;hd\u0026quot;] This selector can find all img elements with a tag attribute and a value of hd. For example, the selector above can select elements such as \u0026lt;img tag=\u0026quot;hd cover\u0026quot;/\u0026gt; and \u0026lt;img tag=\u0026quot;sport football hd\u0026quot;/\u0026gt;.\nMatch attribute values ​​with a certain prefix There are two ways to match prefixes: span[lang|=\u0026quot;zh\u0026quot;]\nThis selector can select all span elements whose lang attribute values ​​start with \u0026lsquo;zh-\u0026rsquo;, such as \u0026lt;span lang=\u0026quot;zh-TW\u0026quot;\u0026gt;, \u0026lt;span lang=\u0026quot;zh-CN\u0026quot;\u0026gt;.\nimg[type^=\u0026quot;low\u0026quot;]\nThis selector can select all img elements whose type attributes start with \u0026rsquo;low\u0026rsquo;, such as \u0026lt;img type=\u0026quot;lowPower\u0026quot;/\u0026gt;, \u0026lt;img type=\u0026quot;lowLevel\u0026quot;/\u0026gt;. The difference between it and the above is that it can match words that are not separated by \u0026lsquo;-\u0026rsquo;.\nMatch attribute values ​​with a certain suffix img[type$=\u0026quot;ball\u0026quot;]\nThis selector can select all img elements whose type attributes end with \u0026lsquo;ball\u0026rsquo;, such as \u0026lt;img type=\u0026quot;football\u0026quot;/\u0026gt;, \u0026lt;img type=\u0026quot;basketball\u0026quot;/\u0026gt;.\nMatch attribute values ​​containing a certain string img[type*=\u0026quot;0\u0026quot;]\nThis selector can select all img elements whose type attribute contains \u0026lsquo;0\u0026rsquo;, such as \u0026lt;img type=\u0026quot;110\u0026quot;/\u0026gt;, \u0026lt;img type=\u0026quot;4008208820\u0026quot;/\u0026gt;.\n:has() pseudo class I am I am 1 2 3 4 5 6 7 8 9 10 \u0026lt;div class=\u0026#34;cont-box\u0026#34;\u0026gt; \u0026lt;!-- Touch these boxes, the disply-box below will display pseudo elements --\u0026gt; \u0026lt;div linkto=\u0026#39;box3\u0026#39;\u0026gt;I am\u0026lt;/div\u0026gt; \u0026lt;div linkto=\u0026#39;box4\u0026#39;\u0026gt;I am\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;disply-box\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .cont-box:has(\u0026gt;[linkto=\u0026#34;box3\u0026#34;]:hover) + .disply-box::after{content:\u0026#39;box3\u0026#39;} .cont-box:has(\u0026gt;[linkto=\u0026#34;box4\u0026#34;]:hover) + .disply-box::after{content:\u0026#39;box4\u0026#39;} \u0026lt;/style\u0026gt; Because the hovered element is wrapped by cont-box, the father\u0026rsquo;s brothers cannot be selected directly through the subsequent sibling selector. We can give the father :has() pseudo-class to indirectly select.\nAbout the :has() selector When the selector in the brackets is true, the pseudo-class will take effect.\nh1:has(+ p)\nIn this example, the adjacent sibling selector is put in. When a h1 tag is followed by a p tag, this pseudo-class will take effect. In this way, you can give styles to the h1 tag before the p tag while other h1 tags are not affected.\n.cont-box:has(\u0026gt;[linkto=\u0026quot;box4\u0026quot;]:hover)\nLet\u0026rsquo;s interpret the selector in the example; the content in the brackets is \u0026gt;[linkto=\u0026quot;box4\u0026quot;]:hover, which means that there is a child element, the linkto attribute value of this child element is box4, and it is being touched by the mouse. Combined with the :has() pseudo-class, it is to find the .cont-box with such a child element and give it a style.\nComplete code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 \u0026lt;div class=\u0026#34;filter\u0026#34;\u0026gt; \u0026lt;input checked type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;all\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;all\u0026#34;\u0026gt;All\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;yellow\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;yellow\u0026#34;\u0026gt;Yellow\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;red\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;red\u0026#34;\u0026gt;Red\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;green\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;green\u0026#34;\u0026gt;Green\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; name=\u0026#34;filter\u0026#34; id=\u0026#34;purple\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;purple\u0026#34;\u0026gt;Purple\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;fruit-cont\u0026#34;\u0026gt; \u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍉\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;🍊\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🍈\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;p\u0026#34;\u0026gt;🍇\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🥝\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;🍋\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;‍🍌\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;🍍\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;y\u0026#34;\u0026gt;🥭\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍎\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🍏\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🍐\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍑\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍒\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;🍓\u0026lt;/div\u0026gt; \u0026lt;div c=\u0026#34;p\u0026#34;\u0026gt;🫐\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;r\u0026#34;\u0026gt;‍🍅\u0026lt;/div\u0026gt;\u0026lt;div c=\u0026#34;g\u0026#34;\u0026gt;🫒\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .filter{display: flex;cursor: pointer;user-select: none;} .filter label{ width: calc(25% - 12px); box-sizing: border-box; margin: 6px; height: 40px; line-height: 36px; text-align: center; border-radius: 8px; border: 2px solid; } .filter input{display: none;} .fruit-cont{display: flex;flex-wrap: wrap;} .fruit-cont div{ display: none; width: calc(25% - 12px); box-sizing: border-box; margin: 6px; height: 60px; line-height: 60px; text-align: center; font-size: 30px; text-shadow: 0 0 6px white; border-radius: 8px; } .filter:has(\u0026gt;#yellow:checked) ~ .fruit-cont div[c=\u0026#34;y\u0026#34;]{display: block} .filter:has(\u0026gt;#green:checked) ~ .fruit-cont div[c=\u0026#34;g\u0026#34;]{display: block} .filter:has(\u0026gt;#red:checked) ~ .fruit-cont div[c=\u0026#34;r\u0026#34;]{display: block} .filter:has(\u0026gt;#purple:checked) ~ .fruit-cont div[c=\u0026#34;p\u0026#34;]{display: block} .filter:has(\u0026gt;#all:checked) ~ .fruit-cont div{display: block} .fruit-cont div[c=\u0026#34;r\u0026#34;]{background-color: #e74c3c} .fruit-cont div[c=\u0026#34;g\u0026#34;]{background-color: #2ecc71} .fruit-cont div[c=\u0026#34;p\u0026#34;]{background-color: #9b59b6} .fruit-cont div[c=\u0026#34;y\u0026#34;]{background-color: #f39c12 } .filter label[for=\u0026#34;red\u0026#34;]{border-color: #e74c3c;color: #e74c3c;} .filter label[for=\u0026#34;green\u0026#34;]{border-color: #2ecc71;color: #2ecc71;} .filter label[for=\u0026#34;purple\u0026#34;]{border-color: #9b59b6;color: #9b59b6;} .filter label[for=\u0026#34;yellow\u0026#34;]{border-color: #f39c12 ;color: #f39c12 ;} .filter label[for=\u0026#34;all\u0026#34;]{border-color: black;color: black ;} .filter input:checked + label[for=\u0026#34;red\u0026#34;]{background-color: #e74c3c;color: white;} .filter input:checked + label[for=\u0026#34;green\u0026#34;]{background-color: #2ecc71;color: white;} .filter input:checked + label[for=\u0026#34;purple\u0026#34;]{background-color: #9b59b6;color: white;} .filter input:checked + label[for=\u0026#34;yellow\u0026#34;]{background-color: #f39c12 ;color: white;} .filter input:checked + label[for=\u0026#34;all\u0026#34;]{background-color: black ;color: white;} \u0026lt;/style\u0026gt; ","date":"2024-10-18T00:00:00Z","image":"https://blog.zhoujump.com/en/p/checkbox-filter/cover.en.webp","permalink":"https://blog.zhoujump.com/en/p/checkbox-filter/","title":"Cleverly use attribute selectors to implement filters in pure CSS"},{"content":" Original article: My resume editor is online\nDrag and drop layout, with two sets of simple and beautiful themes. You can export the resume as an image or call the print function. Free to use, no login required. Welcome to use it.\nEnter now ","date":"2024-10-07T00:00:00Z","image":"https://blog.zhoujump.com/p/resume-tool-index/cover.webp","permalink":"https://blog.zhoujump.com/en/p/resume-tool-index/","title":"My resume editor is online"},{"content":" Original article: Pure CSS to implement the edit and save buttons of the input box\nSee the effect first Click the Edit on the right to activate the input on the left, and click the Finish on the right to save the input box content\npointer-events We all know that the disabled attribute in \u0026lt;input disabled=\u0026quot;true\u0026quot;/\u0026gt; can be used to make the input box uneditable, but as an element attribute, it needs to be modified using js. So how do we achieve this in a pure CSS environment? The answer is to use the pointer-events attribute, which is used to define how an element responds to user clicks. For example, the following two examples:\npointer-events: auto pointer-events: none\n1 2 3 4 5 6 7 8 \u0026lt;!-- This a tag sets pointer-events: auto --\u0026gt; \u0026lt;a style=\u0026#34;pointer-events: auto;\u0026#34; href=\u0026#39;https://blog.zhoujump.com\u0026#39; target=\u0026#34;_blank\u0026#34;\u0026gt;pointer-events: auto\u0026lt;/a\u0026gt; \u0026lt;!-- This a tag sets pointer-events: auto --\u0026gt; \u0026lt;a style=\u0026#34;pointer-events: none;\u0026#34; href=\u0026#39;https://blog.zhoujump.com\u0026#39; target=\u0026#34;_blank\u0026#34;\u0026gt;pointer-events: none\u0026lt;/a\u0026gt; In this example, the a tag with pointer-events: none cannot be clicked, while the a tag with pointer-events: auto can be clicked normally. We can use this method to achieve the effect of disabling the input box.\nComplete code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 \u0026lt;div class=\u0026#34;s-box\u0026#34;\u0026gt; \u0026lt;!-- I am an old tool user. If you don’t understand its usage, you can read the previous article --\u0026gt; \u0026lt;input id=\u0026#34;s-edit\u0026#34; type=\u0026#34;checkbox\u0026#34;/\u0026gt; \u0026lt;input class=\u0026#34;s-input\u0026#34; value=\u0026#34;Click the \u0026#39;Edit\u0026#39; button on the right →\u0026#34;/\u0026gt; \u0026lt;!-- The content of this label is dynamically assigned using pseudo-elements, so leave it blank here --\u0026gt; \u0026lt;label class=\u0026#34;s-label\u0026#34; for=\u0026#34;s-edit\u0026#34;\u0026gt;\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .s-box {//External large box style background: #fff; border: 1px solid #ddd; border-radius: 12px; padding: 10px; transition: all .3s; } .s-box:hover{//External large box style when touched by mouse box-shadow: 0 0 10px #ddd; transition: all .3s; } .s-input,.s-input:focus{//Design input style and remove its default style width:calc(100% - 60px); font-size: 16px; padding: 4px; outline: none; border: none; pointer-events: none;//By default, we make it uneditable border: 1px solid #fff; transition: all .3s; } .s-label{//Right button style width: 40px; cursor: pointer; float: right; text-align: center; } #s-edit:checked ~ .s-input{//When the button is clicked, the checkbox is selected pointer-events: auto;//Make the input editable border: 1px solid #ddd; border-radius: 4px; transition: all .3s; } #s-edit{display: none;}//Hide tool input .s-label::after{content:\u0026#34;Edit\u0026#34;;}//Assign button content through pseudo-element #s-edit:checked ~ .s-label::after{content:\u0026#34;Complete\u0026#34;;}//Assign button content when the checkbox is selected by clicking the button \u0026lt;/style\u0026gt; Related knowledge pointer-events ::after\n","date":"2024-09-29T00:00:00Z","image":"https://blog.zhoujump.com/p/checked_input/cover.webp","permalink":"https://blog.zhoujump.com/en/p/checked_input/","title":"Pure CSS to implement the edit and save buttons of the input box"},{"content":" Original article: Pure CSS to achieve the effect of circular flipping\nSee the effect first Click on the card to switch one, and the entire implementation process does not use js. In addition to the two cards, this sample actually has two transparent inputs. You actually click on these two inputs, and these two inputs will modify their own z-index properties after clicking, ensuring that you will definitely click on another one next time you click.\nz-index After we use positioning (posation:absolute,fixed,relative) on elements, the elements may overlap. In addition to the principle of catching up in the html structure, we can also use z-index to control the covering relationship of elements. The larger the z-index value, the more the element is at the top.\nHere is a short example: Let\u0026rsquo;s try clicking on these two giant radio buttons\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 \u0026lt;div\u0026gt; \u0026lt;input name=\u0026#34;demo1\u0026#34; style=\u0026#34;width:100px;height:100px;position:relative\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input checked name=\u0026#34;demo1\u0026#34; style=\u0026#34;width:100px;height:100px;position:relative;right:50px\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; [name=\u0026#34;demo1\u0026#34;]{ z-index: 1;//Unselected radio button z-index is 1 } [name=\u0026#34;demo1\u0026#34;]:checked{//Selected radio button z-index is 2 z-index: 2; } \u0026lt;/style\u0026gt; The element assigned z-index:2 will cover the element of z-index:1. If we completely overlap these two radio buttons and assign a lower z-index to the selected radio button, we can ensure that the one that is clicked each time is the unselected one. This is the core logic of this flip case.\nLet\u0026rsquo;s get started The following is the full code for the flip card example:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 \u0026lt;div class=\u0026#34;box\u0026#34;\u0026gt; \u0026lt;!-- The two inputs need to be placed before the card but cover it. We use z-index to achieve this --\u0026gt; \u0026lt;input class=\u0026#34;radio1\u0026#34; type=\u0026#34;radio\u0026#34; name=\u0026#34;card\u0026#34;/\u0026gt; \u0026lt;input class=\u0026#34;radio2\u0026#34; type=\u0026#34;radio\u0026#34; name=\u0026#34;card\u0026#34;/\u0026gt; \u0026lt;!-- Two cards --\u0026gt; \u0026lt;div style=\u0026#34;background:#F1948A\u0026#34; class=\u0026#34;card1\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div style=\u0026#34;background:#AED6F1\u0026#34; class=\u0026#34;card2\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; [class*=\u0026#34;card\u0026#34;]{//Set the initial style for the card width: 200px; height: 300px; position: absolute; border-radius: 16px; box-shadow: 0 0 10px rgba(0,0,0,0.2); transition: all 1s; } .box{ height: 300px; width: 200px; position: relative; } [class*=\u0026#34;radio\u0026#34;]{//The width and height of the two radio buttons are consistent with the card, and they are completely transparent, and then covered on the card position: absolute; width: 200px; height: 300px; z-index: 2; margin: 0; opacity: 0; } .radio1:checked ~ .card1{animation: card .6s;top:0;scale:.98;transition-duration:.6s} .radio1:checked ~ .card2{z-index: 1;top:10px;transition-duration:.6s} .radio1:checked{z-index: 0;}//The selected radio button sets z-index to 0 //After being clicked, it will automatically move to the bottom, so that the next click will hit another radio button .radio2:checked ~ .card2{animation: card .6s;top:0;scale:.98;transition-duration:.6s} //Select the card through the subsequent sibling selector, so that selecting a different radio button will control the card to flip one. .radio2:checked ~ .card1{z-index: 1;top:10px;transition-duration:.6s} .radio2:checked{z-index: 0;}//Same as another radio button @keyframes card{//Define a flip animation here 0%{left:0;z-index:2;top:10px} 50%{left:230px;z-index:2;rotate:10deg;} 51%{left:230px;z-index:0;rotate:10deg;} 100%{left:0;z-index:0;scale:.98;} } \u0026lt;/style\u0026gt; But it seems that this can only flip between two cards, so how can we increase the number of cards? At this time, you only need to combine the ideas in Realizing the Rating Component without Using JS. In this case, selecting a star can select all the stars before this star. Similarly, we can set the z-index value of all the previous radio buttons to 0 when selecting a radio button, and reset all z-index after selecting the last radio button to achieve a cyclic flip.\nOf course, the theoretical significance of this case is greater than the practical significance. Every time a card is added, a lot of css code will be generated, which is not as good as js. But this case can still be used as a good case to illustrate the use of z-index.\nRelated knowledge z-index;\n@keyframes;\nanimation\n","date":"2024-09-27T00:00:00Z","image":"https://blog.zhoujump.com/p/index-checked/cover.webp","permalink":"https://blog.zhoujump.com/en/p/index-checked/","title":"Pure CSS to achieve the effect of circular flipping"},{"content":"Original link: https://blog.zhoujump.com/en/p/creat-waifu/\nHow to use Drag the blue square below to the favorites bar and release the mouse, the browser will automatically create a bookmark. Then we open a website at random, click this bookmark, and the poster girl will appear on the web page! This method can only be used for PC browsers, not mobile phones. And some websites, such as Bing, will prohibit loading external resources, and these websites will fail to summon. Baidu, Bilibili, Think No, and Nuggets can all be used. Loading may be a bit slow, so you need to wait patiently.\nHow it is implemented In addition to collecting URLs, the browser\u0026rsquo;s favorites can also collect javascript codes. We can execute the javascript code by clicking on it.\nLike the example below:\n1 2 3 4 5 javascript: alert( \u0026#39;You are looking at:\u0026#39; + document.getElementsByTagName(\u0026#39;title\u0026#39;)[0].innerText ) We copy these codes and create a new bookmark, paste the code into the URL column and save it. Then we open a website at random and click on this bookmark, and a dialog box will pop up to display the title of the current web page. However, if the code you execute has a return value, you need to add a line void(0); at the end of the js code, otherwise clicking the bookmark will jump to another page and display the return value on that page.\nThe principle of summoning the poster girl The code is as follows:\n1 2 3 4 5 6 7 8 javascript: var link = document.createElement(\u0026#39;link\u0026#39;); link.rel = \u0026#39;stylesheet\u0026#39;; link.href = \u0026#39;https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css\u0026#39;; var script = document.createElement(\u0026#39;script\u0026#39;); script.src = \u0026#39;https://fastly.jsdelivr.net/gh/lrplrplrp/bkyl2d@main/loads.js\u0026#39;; document.head.appendChild(link); document.head.appendChild(script); The code adds two tags to the head tag, a link tag and a script tag. They will load the styles required by the poster girl and the initialization code of the poster girl, so that the poster girl will appear on your web page.\nps: Direct dragging and dropping is a good way to bookmark a link with one click.\n","date":"2024-09-27T00:00:00Z","image":"https://blog.zhoujump.com/p/creat-waifu/cover.webp","permalink":"https://blog.zhoujump.com/en/p/creat-waifu/","title":"Summon the poster girl anytime, anywhere with one click."},{"content":" Original article: Pure CSS Rating Component\nSee the effect first The following is a rating component implemented purely by CSS. Click on the star to rate. Different ratings will display different colors.\n★ ★ ★ ★ ★ This component is an upgraded version of the article Realize option selection effect without using js. Some prerequisite knowledge is mentioned in it. Students who are interested can read this article first.\nSubsequent sibling selector ~ In the article Realize option selection effect without using js, we used the adjacent sibling selector +, which can select the next element of the target element. But in the above case, we hope that when clicking on the star, all the stars will become selected. At this time, we need to use the subsequent sibling selector ~, which can select all the sibling elements after the target element.\nLet\u0026rsquo;s look at this simple example:\n★ ★ ★ ★ ★ ★ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;div class=\u0026#34;cont\u0026#34;\u0026gt; \u0026lt;!-- Here we put five stars --\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;star\u0026#34;\u0026gt;★\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .star{//Set the default style for the star color: lightgray; font-size: 24px; } .star:hover ~ .star{//When the mouse moves over a star, all the stars behind it will be selected and set to gold color: gold; font-size: 24px; } \u0026lt;/style\u0026gt; Let\u0026rsquo;s get started Combining the previous case, we add \u0026lt;input class=\u0026quot;radio\u0026quot;\u0026gt;, and then replace \u0026lt;span\u0026gt;★\u0026lt;/span\u0026gt; with \u0026lt;label\u0026gt;★\u0026lt;/label\u0026gt;, it seems that victory is just around the corner.\nThe complete code of the rating component example is as follows:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 \u0026lt;div class=\u0026#34;cont\u0026#34;\u0026gt; \u0026lt;!-- We first create five inputs, which must be placed in front of the stars to facilitate the use of subsequent sibling selectors to select stars --\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star1\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star2\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star3\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star4\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;input class=\u0026#34;radio\u0026#34; name=\u0026#34;star\u0026#34; id=\u0026#34;star5\u0026#34; type=\u0026#34;radio\u0026#34;\u0026gt; \u0026lt;!-- Create five more stars and use for binding with the five inputs above --\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star1\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star2\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star3\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star4\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;label class=\u0026#34;label\u0026#34; for=\u0026#34;star5\u0026#34;\u0026gt;★\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .cont{//Use flexible layout to arrange the stars horizontally position: relative; display: flex; } .radio{//Tool input is still hidden display: none; } .label{//Set the default style for the stars color: #ccc; transition-duration:.3s; font-size: 24px; scale:.9; } #star1:checked ~ .label:nth-child(6), #star2:checked ~ .label:nth-child(-n+7) {//When #star1 is selected by the user, find all the .labels behind it //Use the child selector to select the 6th element (the first five are input) and set its color color:#515A5A; transition-duration:.3s; scale: 1; } #star3:checked ~ .label:nth-child(-n+8), #star4:checked ~ .label:nth-child(-n+9) {//Follow the same method, set the third and fourth stars to blue color:#3498db; transition-duration:.3s; scale: 1; } #star5:checked ~ .label {//The last star does not need a child selector, just select all the stars and set them to gold color:#f1c40f; transition-duration:.3s; scale: 1; } \u0026lt;/style\u0026gt; So we have completed this rating component that does not require js, and can even submit ratings normally without using js. Isn’t it simple?\n","date":"2024-09-25T00:00:00Z","image":"https://blog.zhoujump.com/p/input-star/cover.webp","permalink":"https://blog.zhoujump.com/en/p/input-star/","title":"Pure CSS Rating Component"},{"content":" Original article: Pure CSS to achieve option selection effect\nSee the effect first You can switch different options by clicking, and this example does not use js but is completely implemented with css.\nOption 1 Option 2 Option 3 Option 4 :checked pseudo-class selector The core of this effect is the :checked pseudo-class selector. This selector can match the element clicked by the user, which means that we can set the style for the element selected by the user separately.\nWithout further ado, let\u0026rsquo;s get straight to the code.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 \u0026lt;div class=\u0026#34;cont\u0026#34;\u0026gt; \u0026lt;!-- First create an input element. Although it will not be displayed, we need it to make the element selectable --\u0026gt; \u0026lt;input checked type=\u0026#34;radio\u0026#34; id=\u0026#34;radio1\u0026#34; name=\u0026#34;radio\u0026#34;/\u0026gt; \u0026lt;!-- Then add a label after the input, and bind it to the input above through the for attribute, so that clicking this label is equivalent to clicking the input --\u0026gt; \u0026lt;label for=\u0026#34;radio1\u0026#34;\u0026gt;Option 1\u0026lt;/label\u0026gt; \u0026lt;!-- Do the same for several options --\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; id=\u0026#34;radio2\u0026#34; name=\u0026#34;radio\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;radio2\u0026#34;\u0026gt;Option 2\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; id=\u0026#34;radio3\u0026#34; name=\u0026#34;radio\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;radio3\u0026#34;\u0026gt;Option 3\u0026lt;/label\u0026gt; \u0026lt;input type=\u0026#34;radio\u0026#34; id=\u0026#34;radio4\u0026#34; name=\u0026#34;radio\u0026#34;/\u0026gt; \u0026lt;label for=\u0026#34;radio4\u0026#34;\u0026gt;Option 4\u0026lt;/label\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .cont{ display: flex; // Set a flexible layout for the container so that its internal elements are arranged horizontally. line-height: 120px; text-align: center; } label{ // This is the style of the label. margin-right: 10px; width: 60px; height: 120px; background: #ccc; border-radius: 10px; transition-duration:.3s; } input{ display: none; // Hide the input element, it is just a tool. } input: checked + label{ // Use checked to find the input clicked by the user, and then use the + adjacent sibling selector to select the label next to this input and set the style for it. transition-duration:.3s; width: 120px; background:#99e6ff; color:#006080; } \u0026lt;/style\u0026gt; In the above code, we take advantage of the fact that \u0026lt;input type=\u0026quot;radio\u0026quot;\u0026gt; can be clicked by users, and then bind the label element to it and place it behind it. In this way, not only can input be selected by clicking label, but also label can be selected by input using the adjacent sibling selector. At this time, the input element as a tool can be hidden from the page, and we only need to design the style for the more operable label element. The effect achieved by using this technique is far beyond your imagination.\nRelated knowledge Input radio type;\n:checked pseudo-class selector;\nAdjacent sibling selector;\nlabel and its for attribute\n","date":"2024-09-25T00:00:00Z","image":"https://blog.zhoujump.com/p/checked-css/cover.webp","permalink":"https://blog.zhoujump.com/en/p/checked-css/","title":"Pure CSS to achieve option selection effect"},{"content":" Original article: Add transition animation for elements with uncertain height\nSometimes we encounter this situation where the container is stretched by elements and its height cannot be known, but a smooth transition is required. Like this:\nTry touching my head with your mouse I am the content I am the content I am the content I am the content I believe that CSS beginners have tried this:\n1 2 3 4 5 6 7 8 .cont{ height:0;//The initial height is zero transition: .3s; } .cont:hover{ height:auto;//The height is automatic when the mouse is touched transition: .3s;//Set 0.3 seconds transition } Then I got a cold shoulder. The height does change when the mouse is touched, but the transition does not take effect. This is because transition can only handle transitions between values, and changes between auto - 0 cannot be transitioned.\nNow this transition can be achieved through the calc-size attribute. For details, please see this post, but the compatibility is not very good at present.\nSo what can we do? At this time, we need to bring out our display: grid. The grid layout can control the width-to-height ratio of its internal container through the fr unit. That\u0026rsquo;s a coincidence, folks. We just need to change the height ratio between 1 and 0, right?\nBelow is the code for the head-touching expansion example above:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 \u0026lt;div class=\u0026#34;cont\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;head\u0026#34;\u0026gt;I will form the head\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;body\u0026#34;\u0026gt; I am the content\u0026lt;/br\u0026gt; I am the content\u0026lt;/br\u0026gt; I am the content\u0026lt;/br\u0026gt; I am the content\u0026lt;/br\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .cont{ display:grid;//Set grid layout background:#99e6ff; color:#006080; grid-template-rows: 30px 0fr;//Distribute height: head:30px, body:0% border-radius:8px; transition: .3s; padding:10px; line-height:30px; } .cont:hover{ grid-template-rows: 30px 1fr;//Distribute height: head:30px, body:100% transition: .3s;//Set 0.3 seconds transition } .body{ min-height: 0;//Set the minimum height, otherwise the text can still stretch the container. overflow:hidden;//Set overflow hidden, otherwise the container will be folded and the text will still be there. } \u0026lt;/style\u0026gt; The effect is completed in a simple way. The knowledge about grid layout cannot be explained in a few words. Students who are interested can learn it.\nReference to this article How to make CSS auto height perfectly support transition animation? ","date":"2024-09-24T00:00:00Z","image":"https://blog.zhoujump.com/p/grid-transition/cover.webp","permalink":"https://blog.zhoujump.com/en/p/grid-transition/","title":"Add transition animation for elements with uncertain height"},{"content":" Original article: How to implement an input box whose width changes with the text\nSometimes we need to make an input component whose width changes with the input text, such as the tag input box below:\nMy content is editable 1 2 3 4 5 6 7 8 9 10 11 \u0026lt;div style=\u0026#34;height: 30px; background:#99e6ff; color:#006080; padding:0 8px; border-radius:5px; display:inline-block; line-height:30px; outline: none; margin-bottom: 16px;\u0026#34; contenteditable=\u0026#34;true\u0026#34;\u0026gt;My content is editable\u0026lt;/div\u0026gt; At this time, regular input seems a bit difficult because the width of input is fixed.\nAccording to the above code, we can find that this effect is achieved by an element that cannot be input. The secret lies in the last attribute contenteditable=\u0026quot;true\u0026quot; in style. This attribute can turn the element into an editable text box, but it will not be displayed, so it looks like an ordinary div.\nUsing this attribute reasonably, we can achieve many interesting effects. Make our development efficiency twice as good with half the effort.\n","date":"2024-09-24T00:00:00Z","image":"https://blog.zhoujump.com/p/contenteditable-input/cover.webp","permalink":"https://blog.zhoujump.com/en/p/contenteditable-input/","title":"How to implement an input box whose width changes with the text"},{"content":" Original article: How to add comments to Hugo blog\nForeword: This article continues the previous article and talks about how to add a comment system to your blog. Read the previous article\nDeploy waline This part is introduced in detail on the waline official website. We only need to do Vercel deployment (server). Friends who have purchased domain names can do one more step. After completing the deployment, you can return to this article to continue configuration. Click to go to waline tutorial\nIf you have difficulty accessing vercel, please use magic Internet.\nDeploy hugo We assume that you have configured hugo completely according to the tutorial and have not used other hugo themes.\nEnter the deployed vercel project and select Settings\u0026gt;Domains to go to the domain name management interface and copy a domain name for backup.\nGo to gitlab, find the config/_default/params.toml file, and edit the code starting from line 107\n1 2 3 4 5 6 7 8 9 [comments.waline] serverURL = \u0026#34;domain.com\u0026#34;//Fill in the domain name you copied in the previous step lang = \u0026#34;zh-CN\u0026#34; visitor = \u0026#34;\u0026#34; avatar = \u0026#34;\u0026#34; emoji = [\u0026#34;https://unpkg.com/@waline/emojis@1.1.0/weibo\u0026#34;] meta = [\u0026#39;nick\u0026#39;, \u0026#39;mail\u0026#39;]//Fill in the information you want users to leave requiredMeta = [\u0026#39;nick\u0026#39;]//Fill in the required information for users, and anonymous is allowed if it is empty placeholder = \u0026#34;Leave your comment!\u0026#34; Then there is the code on line 77\n1 2 3 4 ## Comments [comments] enabled = true provider = \u0026#34;waline\u0026#34; For more information about the configuration here, please click here\nAfter editing, save it. Wait for the pipeline to run, and then you can see the comment area appear at the end of the article.\n","date":"2024-09-19T00:00:00Z","image":"https://blog.zhoujump.com/p/hugo-commits/cover.webp","permalink":"https://blog.zhoujump.com/en/p/hugo-commits/","title":"HUGO, Comment!"},{"content":" Original article: HUGO, domain name!\nForeword: This article continues the previous article and talks about how to buy a domain name and then bind our web page to it. Read previous articles\nBuy a domain name Check the domain name Before buying a domain name, the first step should be to check whether the domain name you want to buy is still available. Just like you can\u0026rsquo;t buy baidu.com, because this domain name currently belongs to Baidu, you can only buy those domain names that have not been registered (of course, there is nothing that money can\u0026rsquo;t do).\nThere are many vendors that provide domain name registration. You can Baidu \u0026ldquo;domain name registration\u0026rdquo; and there will be many results. This article uses Tencent Cloud as an example to register a domain name. Click me to jump to Tencent Cloud domain name registration.\nThe current domain name purchase process is very simple. Search for the domain name of interest and the system will give recommendations. By the way, the support for Chinese domain names is now relatively good.\nWe choose a domain name to add to the shopping cart and then purchase it. Of course, if you use Tencent Cloud for the first time, you need to go through the registration and real-name authentication process. Generally speaking, a more favorable price will be given for the first year of registration.\nThen go to the Domain name console to see the domain name you purchased.\nBind to pages Settings in gitlab Return to Deployment\u0026gt;pages and click the New Domain button\nEnter a subdomain of the domain you purchased, and then click Create New Domain\nI purchased zhoujump.com, which is called a root domain. For example, domains like www.zhoujump.com and blog.zhoujump.com are subdomains, and you can name them whatever you want. Of course, you can also use the root domain, but you may encounter trouble in the next step.\nIf you are adding it for the first time, the Verification Status here may be red, it doesn\u0026rsquo;t matter, we will make it green immediately.\nDomain Settings Let\u0026rsquo;s put the page of the previous step aside for now, go to the domain console, find the domain you purchased, and select Resolution.\nThen create two new resolution records and fill them in according to the information given on the previous page. And save.\nAt this time, we wait for a few minutes, because it takes a little time for the resolution to take effect, then go back to the previous page and click the Refresh button, you will find that the verification status turns green. At this time, we can Save changes\nAfter completing these settings, we enter the subdomain name just bound into the browser address bar and press Enter. Your website can now be accessed using the domain name\n","date":"2024-09-17T00:00:00Z","image":"https://blog.zhoujump.com/p/hugo-domain/cover.webp","permalink":"https://blog.zhoujump.com/en/p/hugo-domain/","title":"HUGO, domain name!"},{"content":" Original article: HUGO, start!\nForeword: This article will teach you how to start a hugo project at the speed of light, and host it for free on gitlab pages for everyone to access, just like the website you are seeing now.\nPrepare an account At present, what you need to prepare is very simple, just a gitlab account, if you don\u0026rsquo;t have one, you can click here to register one.\nIf you encounter this situation, you may need magic Internet access. The mainland-only version of Jihu does not provide pages service\nAfter registration, you will be asked to fill in some information. When you fill in this step, you can deploy.\nPull and deploy Create a project Please give the group name a name you like, and then click the Warehouse (URL) button below.\nNext, scroll down and fill in https://github.com/CaiJimmy/hugo-theme-stack-starter.git in Git Repository URL\nProject Name and Project Identifier are also names you like, and then Visibility Level selects \u0026lsquo;Public\u0026rsquo;,\nFinally, click New Project\nStart Project As shown in the figure, click + and then click New File, let\u0026rsquo;s create a new configuration file to start the gitlab pipeline.\nFill in .gitlab-ci.yml in the file name. Enter the following content in the text:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 variables: DART_SASS_VERSION: 1.77.5 HUGO_VERSION: 0.128.0 NODE_VERSION: 20.x GIT_DEPTH: 0 GIT_STRATEGY: clone GIT_SUBMODULE_STRATEGY: recursive TZ: America/Los_Angeles image: name: golang:1.22.1-bookworm pages: script: # Install brotli - apt-get update - apt-get install -y brotli # Install Dart Sass - curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz - tar -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz - cp -r dart-sass/ /usr/local/bin - rm -rf dart-sass* - export PATH=/usr/local/bin/dart-sass:$PATH # Install Hugo - curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb - apt-get install -y ./hugo_extended_${HUGO_VERSION}_linux-amd64.deb - rm hugo_extended_${HUGO_VERSION}_linux-amd64.deb # Install Node.js - curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION} | bash - - apt-get install -y nodejs # Install Node.js dependencies - \u0026#34;[[ -f package-lock.json || -f npm-shrinkwrap.json ]] \u0026amp;\u0026amp; npm ci || true\u0026#34; # Build - hugo --gc --minify # Compress - find public -type f -regex \u0026#39;.*\\.\\(css\\|html\\|js\\|txt\\|xml\\)$\u0026#39; -exec gzip -f -k {} \\; - find public -type f -regex \u0026#39;.*\\.\\(css\\|html\\|js\\|txt\\|xml\\)$\u0026#39; -exec brotli -f -k {} \\; artifacts: paths: - public rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH Then scroll down and select Submit changes.\nNext, we click Build, then click Pipeline, and you can see the project currently being built.\nIf you have just registered a gitlab account, you may be asked to verify your phone number. You need to prepare a non-mainland phone number to receive text messages.\nWhen the pipeline status changes to Passed, we click Deploy, then click Pages, and you can see the URL assigned by gitlab for us.\nWe click this link to open our website.\nIf the interface displays abnormally after opening, you need to go to the config/_default/config.toml file to modify some configurations and change the content in baseurl to the address assigned by gitlab in the previous step. After saving the file, wait for the pipeline to execute again, and then refresh the page, the website should be displayed normally.\nBecause I have bound a domain name here, I filled in the domain name. In the next article, I will teach you how to purchase and bind a domain name.\n","date":"2024-09-14T00:00:00Z","image":"https://blog.zhoujump.com/p/hugo-start/cover.webp","permalink":"https://blog.zhoujump.com/en/p/hugo-start/","title":"HUGO, start!"}]