diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index 20a854fb47..47b4d5f71c 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -42,23 +42,23 @@ export function initRepoIssueTimeTracking() {
 }
 
 async function updateDeadline(deadlineString) {
-  hideElem($('#deadline-err-invalid-date'));
-  $('#deadline-loader').addClass('is-loading');
+  hideElem('#deadline-err-invalid-date');
+  document.getElementById('deadline-loader')?.classList.add('is-loading');
 
   let realDeadline = null;
   if (deadlineString !== '') {
     const newDate = Date.parse(deadlineString);
 
     if (Number.isNaN(newDate)) {
-      $('#deadline-loader').removeClass('is-loading');
-      showElem($('#deadline-err-invalid-date'));
+      document.getElementById('deadline-loader')?.classList.remove('is-loading');
+      showElem('#deadline-err-invalid-date');
       return false;
     }
     realDeadline = new Date(newDate);
   }
 
   try {
-    const response = await POST($('#update-issue-deadline-form').attr('action'), {
+    const response = await POST(document.getElementById('update-issue-deadline-form').getAttribute('action'), {
       data: {due_date: realDeadline},
     });
 
@@ -69,8 +69,8 @@ async function updateDeadline(deadlineString) {
     }
   } catch (error) {
     console.error(error);
-    $('#deadline-loader').removeClass('is-loading');
-    showElem($('#deadline-err-invalid-date'));
+    document.getElementById('deadline-loader').classList.remove('is-loading');
+    showElem('#deadline-err-invalid-date');
   }
 }
 
@@ -87,6 +87,19 @@ export function initRepoIssueDue() {
   });
 }
 
+/**
+ * @param {HTMLElement} item
+ */
+function excludeLabel(item) {
+  const href = item.getAttribute('href');
+  const id = item.getAttribute('data-label-id');
+
+  const regStr = `labels=((?:-?[0-9]+%2c)*)(${id})((?:%2c-?[0-9]+)*)&`;
+  const newStr = 'labels=$1-$2$3&';
+
+  window.location = href.replace(new RegExp(regStr), newStr);
+}
+
 export function initRepoIssueSidebarList() {
   const repolink = $('#repolink').val();
   const repoId = $('#repoId').val();
@@ -123,16 +136,6 @@ export function initRepoIssueSidebarList() {
       fullTextSearch: true,
     });
 
-  function excludeLabel(item) {
-    const href = $(item).attr('href');
-    const id = $(item).data('label-id');
-
-    const regStr = `labels=((?:-?[0-9]+%2c)*)(${id})((?:%2c-?[0-9]+)*)&`;
-    const newStr = 'labels=$1-$2$3&';
-
-    window.location = href.replace(new RegExp(regStr), newStr);
-  }
-
   $('.menu a.label-filter-item').each(function () {
     $(this).on('click', function (e) {
       if (e.altKey) {
@@ -144,9 +147,9 @@ export function initRepoIssueSidebarList() {
 
   $('.menu .ui.dropdown.label-filter').on('keydown', (e) => {
     if (e.altKey && e.keyCode === 13) {
-      const $selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected');
-      if ($selectedItems.length > 0) {
-        excludeLabel($($selectedItems[0]));
+      const selectedItem = document.querySelector('.menu .ui.dropdown.label-filter .menu .item.selected');
+      if (selectedItem) {
+        excludeLabel(selectedItem);
       }
     }
   });
@@ -166,11 +169,11 @@ export function initRepoIssueCommentDelete() {
         const $parentTimelineGroup = $this.closest('.timeline-item-group');
         // Check if this was a pending comment.
         if ($conversationHolder.find('.pending-label').length) {
-          const $counter = $('#review-box .review-comments-counter');
-          let num = parseInt($counter.attr('data-pending-comment-number')) - 1 || 0;
+          const counter = document.querySelector('#review-box .review-comments-counter');
+          let num = parseInt(counter?.getAttribute('data-pending-comment-number')) - 1 || 0;
           num = Math.max(num, 0);
-          $counter.attr('data-pending-comment-number', num);
-          $counter.text(num);
+          counter.setAttribute('data-pending-comment-number', num);
+          counter.textContent = String(num);
         }
 
         $(`#${$this.data('comment-id')}`).remove();
@@ -279,14 +282,16 @@ export function initRepoPullRequestMergeInstruction() {
 }
 
 export function initRepoPullRequestAllowMaintainerEdit() {
-  const $checkbox = $('#allow-edits-from-maintainers');
-  if (!$checkbox.length) return;
+  const checkbox = document.getElementById('allow-edits-from-maintainers');
+  if (!checkbox) return;
 
-  const promptError = $checkbox.attr('data-prompt-error');
+  const $checkbox = $(checkbox);
+
+  const promptError = checkbox.getAttribute('data-prompt-error');
   $checkbox.checkbox({
     'onChange': async () => {
       const checked = $checkbox.checkbox('is checked');
-      let url = $checkbox.attr('data-url');
+      let url = checkbox.getAttribute('data-url');
       url += '/set_allow_maintainer_edit';
       $checkbox.checkbox('set disabled');
       try {
@@ -298,7 +303,7 @@ export function initRepoPullRequestAllowMaintainerEdit() {
         }
       } catch (error) {
         console.error(error);
-        showTemporaryTooltip($checkbox[0], promptError);
+        showTemporaryTooltip(checkbox, promptError);
       } finally {
         $checkbox.checkbox('set enabled');
       }
@@ -325,7 +330,9 @@ export function initRepoIssueReferenceRepositorySearch() {
       },
       onChange(_value, _text, $choice) {
         const $form = $choice.closest('form');
-        $form.attr('action', `${appSubUrl}/${_text}/issues/new`);
+        if (!$form.length) return;
+
+        $form[0].setAttribute('action', `${appSubUrl}/${_text}/issues/new`);
       },
       fullTextSearch: true,
     });
@@ -375,17 +382,16 @@ export function initRepoIssueComments() {
     window.location.reload();
   });
 
-  $(document).on('click', (event) => {
-    const $urlTarget = $(':target');
-    if (!$urlTarget.length) return;
+  document.addEventListener('click', (e) => {
+    const urlTarget = document.querySelector(':target');
+    if (!urlTarget) return;
 
-    const urlTargetId = $urlTarget.attr('id');
+    const urlTargetId = urlTarget.id;
     if (!urlTargetId) return;
+
     if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return;
 
-    const $target = $(event.target);
-
-    if (!$target.closest(`#${urlTargetId}`).length) {
+    if (!e.target.closest(`#${urlTargetId}`)) {
       const scrollPosition = $(window).scrollTop();
       window.location.hash = '';
       $(window).scrollTop(scrollPosition);
@@ -419,30 +425,33 @@ export function initRepoPullRequestReview() {
     if (window.history.scrollRestoration !== 'manual') {
       window.history.scrollRestoration = 'manual';
     }
-    const $commentDiv = $(window.location.hash);
-    if ($commentDiv) {
+    const commentDiv = document.querySelector(window.location.hash);
+    if (commentDiv) {
       // get the name of the parent id
-      const groupID = $commentDiv.closest('div[id^="code-comments-"]').attr('id');
+      const groupID = commentDiv.closest('div[id^="code-comments-"]')?.getAttribute('id');
       if (groupID && groupID.startsWith('code-comments-')) {
         const id = groupID.slice(14);
-        const $ancestorDiffBox = $commentDiv.closest('.diff-file-box');
+        const ancestorDiffBox = commentDiv.closest('.diff-file-box');
         // on pages like conversation, there is no diff header
-        const $diffHeader = $ancestorDiffBox.find('.diff-file-header');
+        const diffHeader = ancestorDiffBox?.querySelector('.diff-file-header');
+
         // offset is for scrolling
         let offset = 30;
-        if ($diffHeader[0]) {
-          offset += $('.diff-detail-box').outerHeight() + $diffHeader.outerHeight();
+        if (diffHeader) {
+          offset += $('.diff-detail-box').outerHeight() + $(diffHeader).outerHeight();
         }
-        $(`#show-outdated-${id}`).addClass('tw-hidden');
-        $(`#code-comments-${id}`).removeClass('tw-hidden');
-        $(`#code-preview-${id}`).removeClass('tw-hidden');
-        $(`#hide-outdated-${id}`).removeClass('tw-hidden');
+
+        document.getElementById(`show-outdated-${id}`).classList.add('tw-hidden');
+        document.getElementById(`code-comments-${id}`).classList.remove('tw-hidden');
+        document.getElementById(`code-preview-${id}`).classList.remove('tw-hidden');
+        document.getElementById(`hide-outdated-${id}`).classList.remove('tw-hidden');
         // if the comment box is folded, expand it
-        if ($ancestorDiffBox.attr('data-folded') && $ancestorDiffBox.attr('data-folded') === 'true') {
-          setFileFolding($ancestorDiffBox[0], $ancestorDiffBox.find('.fold-file')[0], false);
+        if (ancestorDiffBox.getAttribute('data-folded') === 'true') {
+          setFileFolding(ancestorDiffBox, ancestorDiffBox.querySelector('.fold-file'), false);
         }
+
         window.scrollTo({
-          top: $commentDiv.offset().top - offset,
+          top: $(commentDiv).offset().top - offset,
           behavior: 'instant',
         });
       }
@@ -529,7 +538,7 @@ export function initRepoPullRequestReview() {
     const $commentCloud = $td.find('.comment-code-cloud');
     if (!$commentCloud.length && !$ntr.find('button[name="pending_review"]').length) {
       try {
-        const response = await GET($(this).closest('[data-new-comment-url]').attr('data-new-comment-url'));
+        const response = await GET(this.closest('[data-new-comment-url]')?.getAttribute('data-new-comment-url'));
         const html = await response.text();
         $td.html(html);
         $td.find("input[name='line']").val(idx);
@@ -585,6 +594,22 @@ export function initRepoIssueWipToggle() {
   });
 }
 
+async function pullrequest_targetbranch_change(update_url) {
+  const targetBranch = $('#pull-target-branch').data('branch');
+  const $branchTarget = $('#branch_target');
+  if (targetBranch === $branchTarget.text()) {
+    window.location.reload();
+    return false;
+  }
+  try {
+    await POST(update_url, {data: new URLSearchParams({target_branch: targetBranch})});
+  } catch (error) {
+    console.error(error);
+  } finally {
+    window.location.reload();
+  }
+}
+
 export function initRepoIssueTitleEdit() {
   // Edit issue title
   const $issueTitle = $('#issue-title');
@@ -607,23 +632,7 @@ export function initRepoIssueTitleEdit() {
   $('#edit-title').on('click', editTitleToggle);
   $('#cancel-edit-title').on('click', editTitleToggle);
   $('#save-edit-title').on('click', editTitleToggle).on('click', async function () {
-    const pullrequest_targetbranch_change = async function (update_url) {
-      const targetBranch = $('#pull-target-branch').data('branch');
-      const $branchTarget = $('#branch_target');
-      if (targetBranch === $branchTarget.text()) {
-        window.location.reload();
-        return false;
-      }
-      try {
-        await POST(update_url, {data: new URLSearchParams({target_branch: targetBranch})});
-      } catch (error) {
-        console.error(error);
-      } finally {
-        window.location.reload();
-      }
-    };
-
-    const pullrequest_target_update_url = $(this).attr('data-target-update-url');
+    const pullrequest_target_update_url = this.getAttribute('data-target-update-url');
     if (!$editInput.val().length || $editInput.val() === $issueTitle.text()) {
       $editInput.val($issueTitle.text());
       await pullrequest_targetbranch_change(pullrequest_target_update_url);
@@ -631,7 +640,7 @@ export function initRepoIssueTitleEdit() {
       try {
         const params = new URLSearchParams();
         params.append('title', $editInput.val());
-        const response = await POST($(this).attr('data-update-url'), {data: params});
+        const response = await POST(this.getAttribute('data-update-url'), {data: params});
         const data = await response.json();
         $editInput.val(data.title);
         $issueTitle.text(data.title);
@@ -671,10 +680,11 @@ export function initSingleCommentEditor($commentForm) {
   // * normal new issue/pr page, no status-button
   // * issue/pr view page, with comment form, has status-button
   const opts = {};
-  const $statusButton = $('#status-button');
-  if ($statusButton.length) {
+  const statusButton = document.getElementById('status-button');
+  if (statusButton) {
     opts.onContentChanged = (editor) => {
-      $statusButton.text($statusButton.attr(editor.value().trim() ? 'data-status-and-comment' : 'data-status'));
+      const statusText = statusButton.getAttribute(editor.value().trim() ? 'data-status-and-comment' : 'data-status');
+      statusButton.textContent = statusText;
     };
   }
   initComboMarkdownEditor($commentForm.find('.combo-markdown-editor'), opts);